__builtin_expect函数解析

1.引言
在很多源码如Linux内核、Glib等,我们都能看到likely()和unlikely()这两个宏,通常定义如下:

#define likely(x)      __builtin_expect(!!(x), 1)
#define unlikely(x)    __builtin_expect(!!(x), 0)
可以看出这2个宏都是使用函数 __builtin_expect()实现的, __builtin_expect()函数是GCC的一个内建函数(build-in function).

官方的说明如下:

— 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.

大致翻译一下:

exp
    exp 为一个整型表达式, 例如: (ptr != NULL)

c
     c 必须是一个编译期常量, 不能使用变量

返回值
                返回值等于 第一个参数 exp

2.__builtin_expect函数的作用
为什么我们在代码中要使用__builtin_expect函数?它究竟有什么作用呢?

现在处理器都是流水线的,系统可以提前取多条指令进行并行处理,但遇到跳转时,则需要重新取指令,跳转指令打乱了CPU流水线。因此,跳转次数少的程序拥有更高的执行效率。

在C语言编程时,会不可避免地使用if-else分支语句,if else 句型编译后, 一个分支的汇编代码紧随前面的代码,而另一个分支的汇编代码需要使用JMP指令才能访问到。很明显通过JMP访问需要更多的时间, 在复杂的程序中,有很多的if else句型,又或者是一个有if else句型的库函数,每秒钟被调用几万次,通常程序员在分支预测方面做得很糟糕, 编译器又不能精准的预测每一个分支,这时JMP产生的时间浪费就会很大。

因此,引入__builtin_expect函数来增加条件分支预测的准确性,cpu 会提前装载后面的指令,遇到条件转移指令时会提前预测并装载某个分支的指令。编译器会产生相应的代码来优化 cpu 执行效率。

3.__builtin_expect函数的使用方法
通常在if-else分支中使用,首先要明确一点就是 if (exp) 等价于 if (__builtin_expert(exp, x)), 与x的值无关。

if (__builtin_expect((exp), 1))            
{                              
     printf("failed!\r\n"); 
     while(1);                   
}
else
{
    printf("success!\r\n");
}
这段代码的本意是,当exp表达式为真的时候,就要执行if分支,不然就执行else分支。但是我在编写代码的时候知道,当exp表达式为真的概率比较小,所以我使用__builtin_expect函数告诉编译器,让他在编译过程中,会将else分支的代码紧跟着起面的代码,从而减少指令跳转带来的性能上的下降。

更加常用的使用方法是将__builtin_expect指令封装为likely和unlikely宏:

#define likely(x) __builtin_expect(!!(x), 1) //x很可能为真       
#define unlikely(x) __builtin_expect(!!(x), 0) //x很可能为假
首先,看第一个参数!!(x), 他的作用是把(x)转变成"布尔值", 无论(x)的值是多少 !(x)得到的是true或false, !!(x)就得到了原值的"布尔值"。也就是说,使用likely(),执行 if 后面的语句的机会更大,使用 unlikely(),执行 else 后面的语句的机会更大。

ref:

https://www.cnblogs.com/LubinLew/p/GCC-__builtin_expect.html

https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Other-Builtins.html

https://www.jianshu.com/p/2684613a300f

https://blog.csdn.net/carbon06/article/details/80268907
————————————————
版权声明:本文为CSDN博主「wwwlyj123321」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wwwlyj123321/article/details/107905388

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值