Linux-Kernel-Learning
Linux-Kernel-Learning copied to clipboard
__builtin_expect详解
__builtin_expect是GCC(version>=2.9)引进的宏,其作用就是帮助编译器判断条件跳转的预期值,避免跳转造成时间乱费。拿段代码来说:
#define likely(x) __builtin_expect(!!(x), 1) 表示大多数情况下likely(x)条件为真,其存在形式一般为if(likely(x))
#define unlikely(x) __builtin_expect(!!(x), 0) 表示大多数情况下unlikely(x)条件为假,其存在形式为if(unlikely(x))
具体内部汇编实现过程,参看下面程序:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
int test_likely(int x)
{
if(likely(x))
x = 5 ;
else x = 6 ;
return x ;
}
int test_unlikely(int x)
{
if(unlikely(x))
x = 5 ;
else x = 6 ;
return x ;
}
编译:
[hangc@localhost test]$ gcc -fprofile-arcs -O2 -c test_builtin_expect.c
在这里使用了-fprofile-arcs参数,当用gcc编译文件的时候,如果带有-ftest-coverage参数,就会生成这个.gcno文件,它包含了程序块和行号等信息。接下来使用objdump对生成的test_builtin_expect.o进行反汇编,通过汇编代码观察__builtin_expect对程序的影响。
[hangc@localhost test]$ objdump -d test_builtin_expect.o
test_builtin_expect.o: file format elf32-i386
Disassembly of section .text:
00000000 <test_likely>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 45 08 mov 0x8(%ebp),%eax
6: 85 c0 test %eax,%eax -->对eax寄存器中的值按位相与
8: 74 15 je 1f <test_likely+0x1f> -->关键
a: 83 05 00 00 00 00 01 addl $0x1,0x0
11: b8 05 00 00 00 mov $0x5,%eax
16: 83 15 04 00 00 00 00 adcl $0x0,0x4
1d: 5d pop %ebp
1e: c3 ret
1f: 83 05 08 00 00 00 01 addl $0x1,0x8
26: b8 06 00 00 00 mov $0x6,%eax
2b: 83 15 0c 00 00 00 00 adcl $0x0,0xc
32: 5d pop %ebp
33: c3 ret
34: 8d b6 00 00 00 00 lea 0x0(%esi),%esi
3a: 8d bf 00 00 00 00 lea 0x0(%edi),%edi
00000040 <test_unlikely>:
40: 55 push %ebp
41: 89 e5 mov %esp,%ebp
43: 8b 55 08 mov 0x8(%ebp),%edx
46: 85 d2 test %edx,%edx
48: 75 15 jne 5f <test_unlikely+0x1f> -->关键
4a: 83 05 18 00 00 00 01 addl $0x1,0x18
51: b8 06 00 00 00 mov $0x6,%eax
56: 83 15 1c 00 00 00 00 adcl $0x0,0x1c
5d: 5d pop %ebp
5e: c3 ret
5f: 83 05 10 00 00 00 01 addl $0x1,0x10
66: b8 05 00 00 00 mov $0x5,%eax
6b: 83 15 14 00 00 00 00 adcl $0x0,0x14
72: 5d pop %ebp
73: c3 ret
74: 8d b6 00 00 00 00 lea 0x0(%esi),%esi
7a: 8d bf 00 00 00 00 lea 0x0(%edi),%edi
00000080 <_GLOBAL__I_65535_0_test_likely>:
80: 55 push %ebp
81: 89 e5 mov %esp,%ebp
83: 83 ec 18 sub $0x18,%esp
86: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8d: e8 fc ff ff ff call 8e <_GLOBAL__I_65535_0_test_likely+0xe>
92: c9 leave
93: c3 ret
两个函数编译生成的汇编语句所使用到的跳转指令不一样,仔细分析下会发现__builtin_expect实际上是为了满足在大多数情况不执行跳转指令,所以__builtin_expect仅仅是告诉编译器优化,并没有改变其对真值的判断。
这种用法在linux内核中也经常用到,国外也有一篇相关的文章,大家不妨看看:http://kernelnewbies.org/FAQ/LikelyUnlikely