|
在内核代码中常见到两个宏 likely() 和 unlikly() ,它们定义在 include/linux/compiler.h 中:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0) __builtin_expect 是 gcc 的内置函数,它的原型为 long __builtin_expect (long exp, long c) ,第 1 个参数可以是个整数,也可以是个布尔型表达式,函数的返回值为 exp 。使用 __builtin_expect 可以为编译器提供跳转预测信息,关于跳转预测相关内容可参考:http://www.groad.net/bbs/read.php?tid-1455-fpage-3.html 与 http://www.groad.net/bbs/read.php?tid-1456-fpage-3.html 。
该函数的意思是,期望 " exp == c " 。因此,
__builtin_expect(!!(x), 1) 表示 !!(x) 为真的可能性很大,如果 !!(x) 确实为真,那么整个函数的返回值为真。
__builtin_expect(!!(x), 0) 表示 !!(x) 为假的可能性很大,如果 !!(x) 确实为假,那么整个函数的返回值为假。
注意,上面的可能性是程序员所“认为”的,但实际情况不一定就是真或假,然而编译器会按照你所认为的 “可能性” 去安排跳转预测。
下面通过测试程序来验证:
#include <stdio.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
void test_like (int x)
{
if (unlikely(x))
x = x + 8;
else
x = x + 10;
printf ("x = %d\n", x);
}
int main(void)
{
test_like (10);
return (0);
} 编译:gcc -O2 builtin_expect.c -o builtin_expect 反汇编:
08048430 <test_like>:
8048430: 55 push %ebp
8048431: 89 e5 mov %esp,%ebp
8048433: 83 ec 18 sub $0x18,%esp
8048436: 8b 45 08 mov 0x8(%ebp),%eax
8048439: 85 c0 test %eax,%eax
804843b: 75 17 jne 8048454 <test_like+0x24>
804843d: b8 0a 00 00 00 mov $0xa,%eax /* x = x + 10; */
8048442: 89 44 24 04 mov %eax,0x4(%esp)
8048446: c7 04 24 50 85 04 08 movl $0x8048550,(%esp)
804844d: e8 02 ff ff ff call 8048354 <printf@plt>
8048452: c9 leave
8048453: c3 ret
8048454: 83 c0 08 add $0x8,%eax /* x = x + 8 */
8048457: eb e9 jmp 8048442 <test_like+0x12>
8048459: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
将上面程序中的 test_like(10) 中的 10 改为 0 再看反汇编:
08048430 <test_like>:
8048430: 55 push %ebp
8048431: 89 e5 mov %esp,%ebp
8048433: 83 ec 18 sub $0x18,%esp
8048436: 8b 45 08 mov 0x8(%ebp),%eax
8048439: 85 c0 test %eax,%eax
804843b: 75 17 jne 8048454 <test_like+0x24>
804843d: b8 0a 00 00 00 mov $0xa,%eax
8048442: 89 44 24 04 mov %eax,0x4(%esp)
8048446: c7 04 24 50 85 04 08 movl $0x8048550,(%esp)
804844d: e8 02 ff ff ff call 8048354 <printf@plt>
8048452: c9 leave
8048453: c3 ret
8048454: 83 c0 08 add $0x8,%eax
8048457: eb e9 jmp 8048442 <test_like+0x12>
8048459: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
从上面可以看到两个反汇编的内容一样,也就是说不管传递的参数是真或是假,程序跳转预测的安排都只是根据 __builtin_expect 来安排。
因为 unlikely() 是条件很可能为假,那么也就是要执行 x = x + 10 这条语句,所以编译器不安排跳转。从指令缓存的角度来看,跳转很可能是跳转到缓存之外的,这样会减慢程序的执行速度。
同样可以测试 likely() 的情况。
另外需要注意一点区别,unlikely() 用的是 jne 指令,而 likely() 用的是 je 指令。 |
|