1.问题引出
在内核代码的条件分支中的判断中,经常可以看到如下两条语句:
- /* ...... */
- if(unlikely(!something))
- /* ...... */
- if(likely(something))
- /* ...... */
追述一下可以看到有如下的宏定义:
- #define likely(x) __builtin_expect(!!(x), 1)
- #define unlikely(x) __builtin_expect(!!(x), 0)
2.查看gcc的info文档可以看到相关的说明
-- 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 return value is the value of EXP, which should be an integral
expression. The value of C must be a compile-time constant. The
semantics of the built-in are that it is expected that EXP == C.
For example:
if (__builtin_expect (x, 0))
foo ();
would indicate that we do not expect to call `foo', since we
expect `x' to be zero. Since you are limited to integral
expressions for EXP, you should use constructions such as
if (__builtin_expect (ptr != NULL, 1))
error ();
when testing pointer or floating-point values.
大致的意思是这么说的:
你也许会使用'__builtin_expece'为编译器体统分支预测的信息。因为程序员在预测他们的程序
的执行流程和分支走向时,表现得臭名昭著地糟糕,所以在一般情况下,作为一个程序员的你也许
会更喜欢使用gcc的一个参数'-fprofile-arcs'来收集程序运行的关于执行流程和分支走向的实际反馈
信息。然而,从一些比较复杂的程序中收集这些信息是非常困难的。
该内建函数的返回值EXP必须是一个整型的表达式。C的值必须是一个常量。这个内建函数的意思就是
我期望得到的是 EXP == C。
例子:
- if (__builtin_expect (x, 0))
- foo ();
上面表示,我们不期望函数foo()会执行。因为我们期望x的值是0。因为EXP被限制为整型表达式,
所以如果你想测试指针(point)或者浮点数(floating-point),应该采用如下的语法结构:
- if (__builtin_expect (ptr != NULL, 1))
- error ();
上面当中,我们是期望error()函数会被执行。
3.再回到上面看看我们讨论的likely和unlikely
- #define likely(x) __builtin_expect(!!(x), 1)
- #define unlikely(x) __builtin_expect(!!(x), 0)
现在关于likely的返回值就很清晰了,就是期望为1,或者更加可能为1。
同样,unlikely的返回值就是更大可能性是0。
在Linux2.6内核中,看到一对奇怪的函数,likely(), unlikely()。
追踪回去,发现其实是一对宏,如下实现:
#define likely(x) __builtin_expect((x), 1)
#define unlikely(x) __builtin_expect((x), 0)
查了一下是这样说的:
例如: if( likely(val))
if( unlikely (val))
他们都等于if(val),也就是在理解上来说,是等价的。那么为什么要这样用呢?
__builtin_expect() 是 GCC (version >= 2.96)提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。
__builtin_expect((x),1) 表示 x 的值为真的可能性更大;
__builtin_expect((x),0) 表示 x 的值为假的可能性更大。也就是说,使用 likely() ,执行 if 后面的语句 的机会更大,使用unlikely(),执行else 后面的语句的机会更大。
例如下面这段代码,作者就认为 prev 不等于 next 的可能性更大,if (likely(prev != next)) {
next->timestamp = now;
...
} else {
...;
}
通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着起面的代码,从而减少指令跳转带来的性能上的下降。
还有一个有趣的例子:__builtin_expect(!!(x), 1) 这个是说,如果x == 0,那么结果就是 0,如果x == 1, 那么结果就是1,使用了!!是为了让x转化成bool型的。