likely(x)与unlikely(x)函数,即__builtin_expect的使用
Posted HeroKern
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了likely(x)与unlikely(x)函数,即__builtin_expect的使用相关的知识,希望对你有一定的参考价值。
likely(x)与unlikely(x)函数,即__builtin_expect的使用
转载自:http://velep.com/archives/795.html
本文讲的likely()和unlikely()两个宏,在linux内核代码和一些应用中可常见到它们的身影。实质上,这两个宏是关于GCC编译器内置宏__builtin_expect的使用。
顾名思义,likely()指“很有可能”之意,而unlikely()指“不太可能”之意。那么,在实际应用中,它们代表什么?又是怎么使用的呢?下面是一篇外文翻译(加上了本人的一些理解),给出了详细答案。
likely()和unlikely()
对于linux内核代码,在条件判断语句中经常看到likely()和unlikely()的调用,如下代码所示:
bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx); if (unlikely(!bvl)) mempool_free(bio, bio_pool); bio = NULL; goto out;
在这里,调用likely()或unlikely()告诉编译器这个条件很有可能或者不太有可能发生,好让编译器对这个条件判断进行正确地优化。这两个宏在include/linux/compiler.h文件中可以找到:
#define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0)
在GCC文档中可找到上述代码中__builtin_expect的说明,摘录如下:
-- Built-in Function: long __builtin_expect (long EXP, long C) You may use `__builtin_expect' to provide the compiler with branch prediction information. In general, you should prefer to use actual profile feedback for this (`-fprofile-arcs'), as programmers are notoriously bad at predicting how their programs actually perform. However, there are applications in which this data is hard to collect.The </span><span style="color: #0000ff;">return</span> value <span style="color: #0000ff;">is</span><span style="color: #000000;"> the value of EXP, which should be an integral expression. The value of C must be a compile</span>-<span style="color: #000000;">time constant. The semantics of the built</span>-<span style="color: #0000ff;">in</span> are that it <span style="color: #0000ff;">is</span> expected that EXP ==<span style="color: #000000;"> C. For example: </span><span style="color: #0000ff;">if</span> (__builtin_expect (x, <span style="color: #800080;">0</span><span style="color: #000000;">)) foo (); would indicate that we </span><span style="color: #0000ff;">do</span> not expect to call `foo<span style="color: #800000;">'</span><span style="color: #800000;">, since we</span> expect `x<span style="color: #800000;">'</span><span style="color: #800000;"> to be zero. Since you are limited to integral</span> expressions <span style="color: #0000ff;">for</span> EXP, you should use constructions such <span style="color: #0000ff;">as</span> <span style="color: #0000ff;">if</span> (__builtin_expect (ptr != NULL, <span style="color: #800080;">1</span><span style="color: #000000;">)) error (); when testing pointer or floating</span>-point values.</span></pre>
__builtin_expect说明中给出了两示例:
if (__builtin_expect (x, 0)) foo (); 表示期望x == 0,也就是不期望不执行foo()函数;同理,if (__builtin_expect (ptr != NULL, 1)) error (); 表示期望指针prt非空,也就是不期望看到error()函数的执行。
编译器做的优化工作
从GCC的说明中可知,__builtin_expect的主要作用就是:帮助编译器判断条件跳转的预期值,避免因执行jmp跳转指令造成时间浪费。那么它是怎么帮助编译器进行优化的呢?
编译器优化时,根据条件跳转的预期值,按正确地顺序生成汇编代码,把“很有可能发生”的条件分支放在顺序执行指令段,而不是jmp指令段(jmp指令会打乱CPU的指令执行顺序,大大影响CPU指令执行效率)。
下面举例说明。下面这个简单的C程序使用gcc -O2进行编译。
#define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0)
int main(char *argv[], int argc)
int a;
/ 获取输入参数值(编译器不能进行优化) /
a = atoi (argv[1]);
if (unlikely (a == 2))
a++;
else
a–;
printf ("%d\\n", a);
return 0;
使用objdump -S反汇编,查看它的汇编代码。
80483b0 <main>: // 开头 80483b0: 55 push %ebp 80483b1: 89 e5 mov %esp,%ebp 80483b3: 50 push %eax 80483b4: 50 push %eax 80483b5: 83 e4 f0 and $0xfffffff0,%esp // 调用atoi() 80483b8: 8b 45 08 mov 0x8(%ebp),%eax 80483bb: 83 ec 1c sub $0x1c,%esp 80483be: 8b 48 04 mov 0x4(%eax),%ecx 80483c1: 51 push %ecx 80483c2: e8 1d ff ff ff call 80482e4 <atoi@plt> 80483c7: 83 c4 10 add $0x10,%esp // 把输入值与2进行比较,即执行:“a == 2” 80483ca: 83 f8 02 cmp $0x2,%eax // -------------------------------------------------------- // 如果'a' 等于 2 (程序里面认为不太可能), 则跳转, // 否则继续执行, 从而不破坏CPU的指令执行顺序. // -------------------------------------------------------- 80483cd: 74 12 je 80483e1 <main+0x31> 80483cf: 48 dec %eax // 调用printf 80483d0: 52 push %edx 80483d1: 52 push %edx 80483d2: 50 push %eax 80483d3: 68 c8 84 04 08 push $0x80484c8 80483d8: e8 f7 fe ff ff call 80482d4 <printf@plt> // 返回0并退出. 80483dd: 31 c0 xor %eax,%eax 80483df: c9 leave 80483e0: c3 ret
在上面程序中,用likely()代替其中的unlikely(),重新编译,再来看它的汇编代码:
80483b0 <main>: // 开头 80483b0: 55 push %ebp 80483b1: 89 e5 mov %esp,%ebp 80483b3: 50 push %eax 80483b4: 50 push %eax 80483b5: 83 e4 f0 and $0xfffffff0,%esp // 调用atoi() 80483b8: 8b 45 08 mov 0x8(%ebp),%eax 80483bb: 83 ec 1c sub $0x1c,%esp 80483be: 8b 48 04 mov 0x4(%eax),%ecx 80483c1: 51 push %ecx 80483c2: e8 1d ff ff ff call 80482e4 <atoi@plt> 80483c7: 83 c4 10 add $0x10,%esp // -------------------------------------------------- // 如果'a' 等于 2 (程序认为很有可能), 则不跳转,继续执行, // 这样就不破坏CPU的指令执行顺序. // 只有当 a != 2 时才会发生跳转, 而这种情况,程序认为是不太可能的. // --------------------------------------------------- 80483ca: 83 f8 02 cmp $0x2,%eax 80483cd: 75 13 jne 80483e2 <main+0x32> // a++ 指令的优化 80483cf: b0 03 mov $0x3,%al // 调用printf() 80483d1: 52 push %edx 80483d2: 52 push %edx 80483d3: 50 push %eax 80483d4: 68 c8 84 04 08 push $0x80484c8 80483d9: e8 f6 fe ff ff call 80482d4 <printf@plt> // 返回0并退出. 80483de: 31 c0 xor %eax,%eax 80483e0: c9 leave 80483e1: c3 ret
如何使用?
在一个条件判断语句中,当这个条件被认为是非常非常有可能满足时,则使用likely()宏,否则,条件非常非常不可能或很难满足时,则使用unlikely()宏。
参考资料
本文英文原文:http://kernelnewbies.org/FAQ/LikelyUnlikely
更多GCC内置宏或函数,详见:http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
以上是关于likely(x)与unlikely(x)函数,即__builtin_expect的使用的主要内容,如果未能解决你的问题,请参考以下文章
Linux 内核中 likely 与 unlikely 的宏定义解析
Linux 内核源码中likely()和unlikely()
Linux 内核源码中likely()和unlikely()