likely和unlikely是如何对代码的优化?
在执行if判断时,可以使用GCC提供了__builtin_expect对代码进行优化,可以提高代码的运行速度,参考GCC手册的"3.10 Options That Control Optimization".原理是:CPU在执行指令时采用的是流水线的方式,一条指令的执行大致会经过"取码 --> 译码 -->执行",如果在执行时发现需要进行跳转的话,会flush流水线,然后从新的地址重新开始"取码 --> 译码 --> 执行",这个过程会降低代码的执行效率,所以尽量减少跳转的可能(也就是flush流水线的发生频率),就可以提高代码的执行效率 。下面用一个简单的程序为例分析一下。#include <stdio.h> #define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0) void func1(int a)
{
int b; if (unlikely(a >= )) {
b = a + ;
printf("b = %d\n", b);
} else {
b = a + ;
printf("b = %d\n", b);
}
} void func2(int a)
{
int b; if (likely(a >= )) {
b = a + ;
printf("b = %d\n", b);
} else {
b = a + ;
printf("b = %d\n", b);
} } int main(int argc, const char *argv[])
{
int a = ; scanf("a = %d", &a); func1(a);
func2(a); return ;
}likely(x)用于x为真的可能性更大的场景,unlikey(x)用于x为假的可能性更大的场景,这两个宏的最终目的就是尽量减少跳转,因为只要跳转,pipeline就会flush,就会降低效率。
想让上面的优化生效的话,需要指定一定的优化等级,因为默认是-O0,没有任何优化。下面是-O0的反汇编:
00000000004005bc <func1>:
4005bc: a9bd7bfd stp x29, x30, [sp, #-]!
4005c0: 910003fd mov x29, sp
4005c4: b9001fa0 str w0, [x29, #]
4005c8: b9401fa0 ldr w0, [x29, #]
4005cc: 2a2003e0 mvn w0, w0
4005d0: 531f7c00 lsr w0, w0, #
4005d4: 12001c00 and w0, w0, #0xff
4005d8: 92401c00 and x0, x0, #0xff
4005dc: f100001f cmp x0, #0x0
4005e0: b.eq <func1+0x48> // b.none
4005e4: b9401fa0 ldr w0, [x29, #]
4005e8: add w0, w0, #0x1
4005ec: b9002fa0 str w0, [x29, #]
4005f0: adrp x0, <_init-0x430>
4005f4: 911e4000 add x0, x0, #0x790
4005f8: b9402fa1 ldr w1, [x29, #]
4005fc: 97ffffad bl 4004b0 <printf@plt>
: b <func1+0x64>
: b9401fa0 ldr w0, [x29, #]
: add w0, w0, #0x2
40060c: b9002fa0 str w0, [x29, #]
: adrp x0, <_init-0x430>
: 911e4000 add x0, x0, #0x790
: b9402fa1 ldr w1, [x29, #]
40061c: 97ffffa5 bl 4004b0 <printf@plt>
: d503201f nop
: a8c37bfd ldp x29, x30, [sp], #
: d65f03c0 ret 000000000040062c <func2>:
40062c: a9bd7bfd stp x29, x30, [sp, #-]!
: 910003fd mov x29, sp
: b9001fa0 str w0, [x29, #]
: b9401fa0 ldr w0, [x29, #]
40063c: 2a2003e0 mvn w0, w0
: 531f7c00 lsr w0, w0, #
: 12001c00 and w0, w0, #0xff
: 92401c00 and x0, x0, #0xff
40064c: f100001f cmp x0, #0x0
: b.eq <func2+0x48> // b.none
: b9401fa0 ldr w0, [x29, #]
: add w0, w0, #0x1
40065c: b9002fa0 str w0, [x29, #]
: adrp x0, <_init-0x430>
: 911e4000 add x0, x0, #0x790
: b9402fa1 ldr w1, [x29, #]
40066c: 97ffff91 bl 4004b0 <printf@plt>
: b <func2+0x64>
: b9401fa0 ldr w0, [x29, #]
: add w0, w0, #0x2
40067c: b9002fa0 str w0, [x29, #]
: adrp x0, <_init-0x430>
: 911e4000 add x0, x0, #0x790
: b9402fa1 ldr w1, [x29, #]
40068c: 97ffff89 bl 4004b0 <printf@plt>
: d503201f nop
: a8c37bfd ldp x29, x30, [sp], #
: d65f03c0 ret
可以看到,反汇编完全是按照C语言逻辑走的,一五一十,按部就班,上面的优化宏没有起到任何作用。
下面先用-O1看看效果。GCC对-O和-O1的描述是:the compiler tries to reduce code size and execution time, without performing any optimizations that take a great deal of compilation time.
aarch64-linux-gnu-gcc predict.c -o predict -O1
aarch64-linux-gnu-objdump -D predict > predict.S
下面是func1的反汇编结果:
00000000004005bc <func1>:
4005bc: a9bf7bfd stp x29, x30, [sp, #-]!
4005c0: 910003fd mov x29, sp
4005c4: 36f800e0 tbz w0, #, 4005e0 <func1+0x24>
4005c8: add w1, w0, #0x2
4005cc: adrp x0, <_init-0x430>
4005d0: 911c6000 add x0, x0, #0x718
4005d4: 97ffffb7 bl 4004b0 <printf@plt>
4005d8: a8c17bfd ldp x29, x30, [sp], #
4005dc: d65f03c0 ret
4005e0: add w1, w0, #0x1
4005e4: adrp x0, <_init-0x430>
4005e8: 911c6000 add x0, x0, #0x718
4005ec: 97ffffb1 bl 4004b0 <printf@plt>
4005f0: 17fffffa b 4005d8 <func1+0x1c>
func1的代码里,unlikely(a >= 0)的可能性小,所以为了减少跳转,就应该将else分支里的代码往前放,这样指令就可以一条紧挨着一条执行,不用跳转,即PC每次加4,pipeline不用flush,提高了代码执行速度。与之相反的是func2中,likely(a >= 0)的可能性更大,为了减少分支跳转,所以需要将if分支对应的代码放在前面。下面是func2的反汇编:
00000000004005f4 <func2>:
4005f4: a9bf7bfd stp x29, x30, [sp, #-]!
4005f8: 910003fd mov x29, sp
4005fc: 37f800e0 tbnz w0, #, <func2+0x24>
: add w1, w0, #0x1
: adrp x0, <_init-0x430>
: 911c6000 add x0, x0, #0x718
40060c: 97ffffa9 bl 4004b0 <printf@plt>
: a8c17bfd ldp x29, x30, [sp], #
: d65f03c0 ret
: add w1, w0, #0x2
40061c: adrp x0, <_init-0x430>
: 911c6000 add x0, x0, #0x718
: 97ffffa3 bl 4004b0 <printf@plt>
: 17fffffa b <func2+0x1c>
当然,如果likely和unlikely用的不符合实际情况,代码的执行效率更恶化。
00000000004005f8 <func1>:
4005f8: adrp x2, <_init-0x430>
4005fc: 36f80080 tbz w0, #, 40060c <func1+0x14>
: add w1, w0, #0x2
: 911ba040 add x0, x2, #0x6e8
: 17ffffaa b 4004b0 <printf@plt>
40060c: add w1, w0, #0x1
: 911ba040 add x0, x2, #0x6e8
: 17ffffa7 b 4004b0 <printf@plt> <func2>:
: adrp x2, <_init-0x430>
40061c: 37f80080 tbnz w0, #, 40062c <func2+0x14>
: add w1, w0, #0x1
: 911ba040 add x0, x2, #0x6e8
: 17ffffa2 b 4004b0 <printf@plt>
40062c: add w1, w0, #0x2
: 911ba040 add x0, x2, #0x6e8
: 17ffff9f b 4004b0 <printf@plt>
-O3:Optimize yet more. ‘-O3’ turns on all optimizations specified by ‘-O2’ and also turns on more optimization flags00000000004005f8 <func1>:
4005f8: adrp x2, <_init-0x430>
4005fc: 36f80080 tbz w0, #, 40060c <func1+0x14>
: add w1, w0, #0x2
: 911ba040 add x0, x2, #0x6e8
: 17ffffaa b 4004b0 <printf@plt>
40060c: add w1, w0, #0x1
: 911ba040 add x0, x2, #0x6e8
: 17ffffa7 b 4004b0 <printf@plt> <func2>:
: adrp x2, <_init-0x430>
40061c: 37f80080 tbnz w0, #, 40062c <func2+0x14>
: add w1, w0, #0x1
: 911ba040 add x0, x2, #0x6e8
: 17ffffa2 b 4004b0 <printf@plt>
40062c: add w1, w0, #0x2
: 911ba040 add x0, x2, #0x6e8
: 17ffff9f b 4004b0 <printf@plt>
00000000004005f4 <func1>:
4005f4: adrp x2, <_init-0x430>
4005f8: 37f80080 tbnz w0, #, <func1+0x14>
4005fc: add w1, w0, #0x1
: 911b8040 add x0, x2, #0x6e0
: 17ffffab b 4004b0 <printf@plt>
: add w1, w0, #0x2
40060c: 17fffffd b <func1+0xc> <func2>:
: adrp x2, <_init-0x430>
: 37f80080 tbnz w0, #, <func2+0x14>
: add w1, w0, #0x1
40061c: 911b8040 add x0, x2, #0x6e0
: 17ffffa4 b 4004b0 <printf@plt>
: add w1, w0, #0x2
: 17fffffd b 40061c <func2+0xc>
-Os主要是对代码尺寸的优化(可以看到,此时两个func反汇编出来的汇编指令是最少的),但是从执行效率看,就差点,likely和unlikey此时对代码没有起到任何优化效果。
likely和unlikely是如何对代码的优化?的更多相关文章
- Java开发代码性能优化总结
代码优化,可能说起来一些人觉得没用.可是我觉得应该平时开发过程中,就尽量要求自己,养成良好习惯,一个个小的优化点,积攒起来绝对是有大幅度效率提升的.好了,将平时看到用到总结的分享给大家. 代码优化的目 ...
- Java开发中程序和代码性能优化
现在计算机的处理性能越来越好,加上JDK升级对一些代码的优化,在代码层针对一些细节进行调整可能看不到性能的明显提升, 但是我觉得在开发中注意这些,更多的是可以保持一种性能优先的意识,对一些敲代码时间比 ...
- Python 代码性能优化技巧(转)
原文:Python 代码性能优化技巧 Python 代码优化常见技巧 代码优化能够让程序运行更快,它是在不改变程序运行结果的情况下使得程序的运行效率更高,根据 80/20 原则,实现程序的重构.优化. ...
- Android代码内存优化建议-OnTrimMemory优化
原文 http://androidperformance.com/2015/07/20/Android代码内存优化建议-OnTrimMemory优化/ OnTrimMemory 回调是 Androi ...
- JAVA_eclipse 保留Java文件时自动格式化代码和优化Import
Eclipse 保存Java文件时自动格式化代码和优化Import Eclipse中format代码的快捷方式是ctrl+shift+F,如果大家想保存 java文件的时候 自动就格式化代码+消除不必 ...
- JavaScript代码性能优化总结
JavaScript 代码性能优化总结 尽量使用源生方法 javaScript是解释性语言,相比编译性语言执行速度要慢.浏览器已经实现的方法,就不要再去实现一遍了.另外,浏览器已经实现的方法在算法方面 ...
- Html代码seo优化最佳布局实例讲解
搜索引擎对html代码是非常优化的,所以html的优化是做好推广的第一步.一个符合seo规则的代码大体如下界面所示. 1.<!–木庄网络博客–> 这个东西是些页面注释的,可以在这里加我的& ...
- 利用封装、继承对Java代码进行优化
注:本文实例分别可以在oldcastle(未优化的代码)和newcastle(优化后的代码)中查看,网址见文末 城堡游戏: 城堡中有多个房间,用户通过输入north, south, east, wes ...
- java代码之美(11)---java代码的优化
java代码的优化 随着自己做开发时间的增长,越来越理解雷布斯说的: 敲代码要像写诗一样美.也能理解有一次面试官问我你对代码有洁癖吗? 一段好的代码会让人看就像诗一样,也像一个干净房间会让人看去很舒服 ...
- 嵌入式程序设计中C/C++代码的优化
虽然使软件正确是一个工程合乎逻辑的最后一个步骤,但是在嵌入式的系统开发中,情况并不总是这样的.出于对低价产品的需求,硬件的设计者需要提供刚好足够的存储器和完成工作的处理能力.所以在嵌入式软件设计的最后 ...
随机推荐
- spark基础知识四
围绕spark的其他特性和应用.主要包括以下几个方面 spark自定义分区 spark中的共享变量 spark程序的序列化问题 spark中的application/job/stage/task之间的 ...
- vs启动报4.X的错
参考: https://www.cnblogs.com/zsx-blog/p/6136956.html https://blog.csdn.net/lishaoran369/article/detai ...
- Sql Server怎样设置sa用户登录
首先,我门打开数据库管理工具,用windows方式登录,windows只能本机登录,这样远程的客户端就登录不了,我们目前就是为了开启sa登录,使远程客户端也能访问数据库,看下图,windows方式登录 ...
- kaggle house price
kaggle 竞赛入门 导入常用的数据分析以及模型的库 数据处理 Data fields 去除异常值 处理缺失值 分析 Utilities Exploratory Data Analysis Corr ...
- maven site 命令报错
在执行 mvn site 命令时报错: java.lang.NoClassDefFoundError: org/apache/maven/doxia/siterenderer/DocumentCont ...
- linux系统ubuntu中在命令行如何打开图形界面的文件夹
用linux查看文件列表之类的受到命令行限制,还是不太方便的.在文件夹中打开的话,切换路径又没有linux终端快,于是,需要在命令行窗口中打开文件夹.如何做呢? 来到终端命令行中,cd切换你的路径,使 ...
- thinkphp漏洞如何修复
THINKPHP漏洞修复,官方于近日,对现有的thinkphp5.0到5.1所有版本进行了升级,以及补丁更新,这次更新主要是进行了一些漏洞修复,最严重的就是之前存在的SQL注入漏洞,以及远程代码执行查 ...
- Qt SDK Issue cstdlib: fatal error: stdlib.h: No such file or directory
*To reproduce the issue, I've tried the following solutions which did not help:* *1) Globally remove ...
- Lambda表达式和方法引用
1 , 为什么用lambda表达式 将重复固定的代码写法简单化 2 ,lambda表达式的实质 对函数式接口的实现(一个接口中只有一个抽象方法的接口被称为函数式接口) package com.mo ...
- 【转帖】nmap命令总结
nmap命令总结 https://www.cnblogs.com/chenqionghe/p/10657722.html 一.nmap是什么 nmap是一款网络扫描和主机检测的非常有用的工具,不局限于 ...