C语言进阶——likely和unlikely
在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习。我的使命就是过滤掉大量的垃圾信息,将知识体系化,以短平快的方式直达问题本质,把大家从大海捞针的痛苦中解脱出来。
文章目录
- 1 定义
- 2 应用场景
- 3 注意事项
- 4 作用机理
- 4.1 理论
- 4.2 实践
- 4.2.1 不使用likely或unlikely选项
- 4.2.2 使用likely选项
- 4.2.3 使用unlikely选项
1 定义
1 #define likely(x) __builtin_expect(!!(x), 1)2 #define unlikely(x) __builtin_expect(!!(x), 0)
__builtin_expect是编译器内建函数,原型为long __builtin_expect (long exp, long c)。该函数并不会改变exp的值,但是可以对if-else分支或者if分支结构进行优化。likely代表if分支大概率会发生,unlikely代表if分支大概率不会发生。
Tips: !!是C语言中处理逻辑表达式的一个技巧。因为C语言中没有布尔变量,所以布尔值是用整形来代替的,0为假,非0为真。当x为0时,!(x)为1,!!(x)为0,!!的运算没有什么意义;但当x为非0时(比如100),!(x)为0,!!(x)为1,这样就达到了将非0值(比如100)全部都映射为1的效果。
通过分析发现,unlikely的定义其实是可以不使用!!运算符的。
2 应用场景
总的来说,对代码运行效率有要求的if-else或if分支就应该使用likely或unlikely优化选项。
3 注意事项
- likely和unlikely的概率判断务必准确,不要写反了,否则非但不能提升运行效率,反而会起到反作用。
- 选择表达式时要选择编译阶段编译器无法推测出真假的表达式,否则优化不起作用。
- 编译时需要至少使用-O2选项,否则优化不起作用。
4 作用机理
4.1 理论
使用likely或unlikely为什么会起到提升代码运行效率的优化效果呢?
主要的作用机理有以下2点:
- gcc编译器在编译生成汇编代码时会在编译选项的引导下调整if分支内代码的位置,如果是likely修饰过的就调整到前面,如果是unlikely修饰过的就调整到后面。放到前面的代码可以节省跳转指令带来的时间开销,从而达到提升效率的目的。
- 当代CPU都有ICache和流水线机制,在运行当前这条指令时,ICache会预读取后面的指令,以提升运行效率。但是如果条件分支的结果是跳转到了其他指令,那预取的下一条指令(有的CPU设计是4级流水,也就是4条指令)就没用了,这样就降低了流水线的效率。如果使用likely和unlikely来指导编译器总是将大概率执行的代码放在靠前的位置,就可以大大提高预取值的命中率,从而达到提升效率的目的。
4.2 实践
下面通过一个小栗子来感受一下likely和unlikely的行为。
期间使用的工具有gcc和objdump。涉及到的指令如下:
# 编译生成a.out,注意使用-O2选项,否则不生效
gcc -O2 test.c
# 根据生成的a.out生成反汇编代码
objdump -CS a.out > objdump.txt
Tips: objdump命令是用查看目标文件或者可执行的目标文件的构成的gcc工具
(1)-d:反汇编目标文件中包含的可执行指令。
(2)-S:混合显示源码和汇编代码,前提是在编译目标文件时加上-g,否则相当于-d。
(3)-C:一般针对C++语言,用来更友好地显示符号名。
4.2.1 不使用likely或unlikely选项
/* 未使用likely或unlikely选项 */4 int main(int argc, char *argv[])5 { 6 int i = atoi(argv[1]); /* init i with the value that GCC can't optimize */7 8 if (i > 0){9 i--;10 }else{11 i++;12 }13 14 return i;15 }
! 未使用likely或unlikely的反汇编代码45 08048320 <main>:46 8048320: 55 push %ebp47 8048321: 89 e5 mov %esp,%ebp48 8048323: 83 e4 f0 and $0xfffffff0,%esp49 8048326: 83 ec 10 sub $0x10,%esp50 8048329: 8b 45 0c mov 0xc(%ebp),%eax51 804832c: 8b 40 04 mov 0x4(%eax),%eax52 804832f: 89 04 24 mov %eax,(%esp)53 8048332: e8 d9 ff ff ff call 8048310 <atoi@plt>54 8048337: c9 leave55 8048338: 8d 50 ff lea -0x1(%eax),%edx !注意到if分支内容在前56 804833b: 85 c0 test %eax,%eax57 804833d: 8d 48 01 lea 0x1(%eax),%ecx !注意到else分支内容在后58 8048340: 0f 4e d1 cmovle %ecx,%edx59 8048343: 89 d0 mov %edx,%eax60 8048345: c3 ret
4.2.2 使用likely选项
1 #define likely(x) __builtin_expect(!!(x), 1)2 #define unlikely(x) __builtin_expect(!!(x), 0)3 4 int main(int argc, char *argv[])5 { 6 int i = atoi(argv[1]); /* init i with the value that GCC can't optimize */7 8 if (likely(i > 0)){9 i--;10 }else{11 i++;12 }13 14 return i;15 }
! 使用likely后的反汇编代码45 08048320 <main>:46 8048320: 55 push %ebp47 8048321: 89 e5 mov %esp,%ebp48 8048323: 83 e4 f0 and $0xfffffff0,%esp49 8048326: 83 ec 10 sub $0x10,%esp50 8048329: 8b 45 0c mov 0xc(%ebp),%eax51 804832c: 8b 40 04 mov 0x4(%eax),%eax52 804832f: 89 04 24 mov %eax,(%esp)53 8048332: e8 d9 ff ff ff call 8048310 <atoi@plt>54 8048337: 85 c0 test %eax,%eax55 8048339: 7e 05 jle 8048340 <main+0x20>56 804833b: 83 e8 01 sub $0x1,%eax !注意到if分支内容在前57 804833e: c9 leave58 804833f: c3 ret59 8048340: 83 c0 01 add $0x1,%eax !注意到else分支内容在后60 8048343: c9 leave61 8048344: c3 ret
4.2.3 使用unlikely选项
1 #define likely(x) __builtin_expect(!!(x), 1)2 #define unlikely(x) __builtin_expect(!!(x), 0)3 4 int main(int argc, char *argv[])5 { 6 int i = atoi(argv[1]); /* init i with the value that GCC can't optimize */7 8 if (unlikely(i > 0)){9 i--;10 }else{11 i++;12 13 14 return i;15 }
! 使用unlikely选项的反汇编结果45 08048320 <main>:46 8048320: 55 push %ebp47 8048321: 89 e5 mov %esp,%ebp48 8048323: 83 e4 f0 and $0xfffffff0,%esp49 8048326: 83 ec 10 sub $0x10,%esp50 8048329: 8b 45 0c mov 0xc(%ebp),%eax51 804832c: 8b 40 04 mov 0x4(%eax),%eax52 804832f: 89 04 24 mov %eax,(%esp)53 8048332: e8 d9 ff ff ff call 8048310 <atoi@plt>54 8048337: 85 c0 test %eax,%eax55 8048339: 7f 05 jg 8048340 <main+0x20>56 804833b: 83 c0 01 add $0x1,%eax !注意到else分支内容被提前了57 804833e: c9 leave58 804833f: c3 ret59 8048340: 83 e8 01 sub $0x1,%eax !注意到if分支内容被后移了60 8048343: c9 leave61 8048344: c3 ret
<完>
C语言进阶——likely和unlikely相关推荐
- ggplot2箱式图两两比较_R语言进阶笔记2 | 长数据与ggplot2
1. 长数据是什么鬼? 之前介绍了如何将多个性状的箱线图放在一个图上,比如learnasreml包中的fm数据,它有h1~h5五年的株高数据,想对它进行作图. 「数据预览:」 > library ...
- C语言进阶深度学习目录表
学习交流加(可免费帮忙下载CSDN资源): 个人微信(进微信群加): LyyCoder 学习交流资源分享qq群1(已满): 962535112 学习交流资源分享qq群2(已满): 780902027 ...
- 【C语言进阶深度学习记录】三十八 C/C++语言中的函数声明与函数定义
文章目录 1 函数的声明和定义 1.1 代码分析 2 总结 1 函数的声明和定义 声明的意义在于告诉编译器程序单元的存在.只是告诉编译器它存在但是不在声明这里定义,有可能在当前文件中的其他地方或者其他 ...
- 【C语言进阶深度学习记录】三十五 程序中的堆、栈以及静态存储区(数据区)
学习交流加 个人qq: 1126137994 个人微信: liu1126137994 学习交流资源分享qq群: 962535112 在我之前学习底层的知识的时候,也写过相关的内容.可以对比的学习:[软 ...
- 【C语言进阶深度学习记录】二十六 C语言中的字符串与字符数组的详细分析
之前有一篇文章是学习了字符和字符串的,可以与之结合学习:[C语言进阶深度学习记录]十二 C语言中的:字符和字符串 文章目录 1 字符串的概念 1.1 字符串与字符数组 1.2 字符数组与字符串代码分析 ...
- 【C语言进阶深度学习记录】十九 #pragma使用与分析
文章目录 1 #pragma 概念简介 1.1 #pragma message 的用法 1.2 #pragma once 的用法 1.3 #pragma pack 的用法 1.31 struct占用的 ...
- 【C语言进阶深度学习记录】十六 静态库与动态库的创建与使用
上一篇文章学习了编译的过程,点击链接查看:[C语言进阶深度学习记录]十五 编译过程简介,每一个C源文件编译后将会生成目标文件,那么这些目标文件,还需要链接起来,生成可执行文件. 文章目录 1 链接的意 ...
- 【C语言进阶深度学习记录】八 C语言中void的分析
文章目录 1 void的意义 1.1 不存在void变量 1.2 C标准 1.3 void指针的意义 1.4 通过void* 实现memset函数 2 总结 1 void的意义 void修饰函数的参数 ...
- 【C语言进阶深度学习记录】五 C语言中变量的属性
上一篇文章学习了C语言中的类型转换,点击链接查看:[C语言进阶深度学习记录]四 C语言中的类型转换. 文章目录 1 C语言的变量属性 1.1 auto关键字 1.2 register关键字 1.3 s ...
- c语言中通过键盘给一维数组赋值怎么_编程C语言进阶篇——构造类型:数组
在c语言的实际应用中,我们常常需要同时对多个数据进行处理,如果没有专门批量处理数据的方法,就会给我们的程序编写造成巨大的影响.而这种批量处理数据的方法就是我们这次的主题--数组. 一.数组是什么 在c ...
最新文章
- Opencv 图片融合 addWeighted性能测试
- 显卡mx150和230哪个好_MX250显卡等于GTX1050?笔记本显卡MX250和MX150的区别对比
- 不断提升自己创造溢价的能力
- 给mysql的root %用户添加grant权限。并给创建的用户赋予权限
- 答疑:有关一例EXT3文件系统故障
- 4G DTU使用教程
- 【IOI2018】狼人【Kruscal重构树】【主席树】
- 两家大型网贷平台竟在借款人审核问题上“偷懒”?
- 【转】sql语句优化工具LECCO SQL Expert
- 8款最受欢迎的HTML5/CSS3应用及源码
- SQL日志文件损坏或丢失造成数据库置疑的解决办法
- 16进制颜色码对照表
- segue 分析小结
- “最新”手机号码归属地库制作
- 独木舟上的旅行(船问题贪心)
- 东南大学计算机科学与工程学院在哪个校区,2021年东南大学有几个校区,大一新生在哪个校区...
- 计算机软件商标,计算机软件商标的类别号 是多少?
- 【SemiDrive源码分析】【MailBox核间通信】43 - 基于Mailbox IPCC RPC 实现核间通信(代码实现篇)
- id 查找apple_厉害!竟能利用苹果ID锁找回丢失手机?
- Docker命令汇总
热门文章
- Excel:只要年月日不要时分秒
- 【java毕业设计】基于java+swing+CS的图书销售管理系统GUI设计与实现(毕业论文+程序源码)——图书销售管理系统
- 做c语言时经常遇到的错误,C语言新人常见问题与错误
- 端口扫描分析(一)常用的网络相关命令
- java hybris_Hybris UI的Route(路由)实现
- mysql主从复制轮训,MySQL主从配置 - 李小热_大风起兮云飞扬 - OSCHINA - 中文开源技术交流社区...
- chatgpt赋能python:如何去除Python列表中的中括号
- 【教学类-33-02】20230518食物加加加2.0版(3、4、5、10、15、20以内数字加法,随机抽数)
- XQuery查询语言及应用实例分析
- 我的英语学习计划(修订版2006.4.7)