赞
踩
在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习。我的使命就是过滤掉大量的垃圾信息,将知识体系化,以短平快的方式直达问题本质,把大家从大海捞针的痛苦中解脱出来。
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的定义其实是可以不使用!!运算符的。
总的来说,对代码运行效率有要求的if-else或if分支就应该使用likely或unlikely优化选项。
使用likely或unlikely为什么会起到提升代码运行效率的优化效果呢?
主要的作用机理有以下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++语言,用来更友好地显示符号名。
/* 未使用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 %ebp
47 8048321: 89 e5 mov %esp,%ebp
48 8048323: 83 e4 f0 and $0xfffffff0,%esp
49 8048326: 83 ec 10 sub $0x10,%esp
50 8048329: 8b 45 0c mov 0xc(%ebp),%eax
51 804832c: 8b 40 04 mov 0x4(%eax),%eax
52 804832f: 89 04 24 mov %eax,(%esp)
53 8048332: e8 d9 ff ff ff call 8048310 <atoi@plt>
54 8048337: c9 leave
55 8048338: 8d 50 ff lea -0x1(%eax),%edx !注意到if分支内容在前
56 804833b: 85 c0 test %eax,%eax
57 804833d: 8d 48 01 lea 0x1(%eax),%ecx !注意到else分支内容在后
58 8048340: 0f 4e d1 cmovle %ecx,%edx
59 8048343: 89 d0 mov %edx,%eax
60 8048345: c3 ret
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 %ebp
47 8048321: 89 e5 mov %esp,%ebp
48 8048323: 83 e4 f0 and $0xfffffff0,%esp
49 8048326: 83 ec 10 sub $0x10,%esp
50 8048329: 8b 45 0c mov 0xc(%ebp),%eax
51 804832c: 8b 40 04 mov 0x4(%eax),%eax
52 804832f: 89 04 24 mov %eax,(%esp)
53 8048332: e8 d9 ff ff ff call 8048310 <atoi@plt>
54 8048337: 85 c0 test %eax,%eax
55 8048339: 7e 05 jle 8048340 <main+0x20>
56 804833b: 83 e8 01 sub $0x1,%eax !注意到if分支内容在前
57 804833e: c9 leave
58 804833f: c3 ret
59 8048340: 83 c0 01 add $0x1,%eax !注意到else分支内容在后
60 8048343: c9 leave
61 8048344: c3 ret
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 %ebp
47 8048321: 89 e5 mov %esp,%ebp
48 8048323: 83 e4 f0 and $0xfffffff0,%esp
49 8048326: 83 ec 10 sub $0x10,%esp
50 8048329: 8b 45 0c mov 0xc(%ebp),%eax
51 804832c: 8b 40 04 mov 0x4(%eax),%eax
52 804832f: 89 04 24 mov %eax,(%esp)
53 8048332: e8 d9 ff ff ff call 8048310 <atoi@plt>
54 8048337: 85 c0 test %eax,%eax
55 8048339: 7f 05 jg 8048340 <main+0x20>
56 804833b: 83 c0 01 add $0x1,%eax !注意到else分支内容被提前了
57 804833e: c9 leave
58 804833f: c3 ret
59 8048340: 83 e8 01 sub $0x1,%eax !注意到if分支内容被后移了
60 8048343: c9 leave
61 8048344: c3 ret
<完>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。