gcc的内建函数__builtin_expect

1.问题引出

在内核代码的条件分支中的判断中,经常可以看到如下两条语句:

[cpp]  view plain copy
  1. /* ...... */  
  2. if(unlikely(!something))  
  3. /* ...... */  
  4. if(likely(something))  
  5. /* ...... */  

追述一下可以看到有如下的宏定义:

[cpp]  view plain copy
  1. #define likely(x)   __builtin_expect(!!(x), 1)  
  2. #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。

 

例子:

[cpp]  view plain copy
  1. if (__builtin_expect (x, 0))  
  2.   foo ();  

上面表示,我们不期望函数foo()会执行。因为我们期望x的值是0。因为EXP被限制为整型表达式,

所以如果你想测试指针(point)或者浮点数(floating-point),应该采用如下的语法结构:

[cpp]  view plain copy
  1. if (__builtin_expect (ptr != NULL, 1))  
  2.   error ();  

上面当中,我们是期望error()函数会被执行。

 

3.再回到上面看看我们讨论的likely和unlikely

[cpp]  view plain copy
  1. #define likely(x)   __builtin_expect(!!(x), 1)  
  2. #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型的。

expect(!!(x), 1

__builtin_expect(!!(x), 1)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值