博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
likely(x)与unlikely(x)函数,即__builtin_expect的使用
阅读量:5983 次
发布时间:2019-06-20

本文共 5982 字,大约阅读时间需要 19 分钟。

转载自: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 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_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 
:// 开头80483b0: 55 push %ebp80483b1: 89 e5 mov %esp,%ebp80483b3: 50 push %eax80483b4: 50 push %eax80483b5: 83 e4 f0 and $0xfffffff0,%esp// 调用atoi()80483b8: 8b 45 08 mov 0x8(%ebp),%eax80483bb: 83 ec 1c sub $0x1c,%esp80483be: 8b 48 04 mov 0x4(%eax),%ecx80483c1: 51 push %ecx80483c2: e8 1d ff ff ff call 80482e4
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
80483cf: 48 dec %eax// 调用printf80483d0: 52 push %edx80483d1: 52 push %edx80483d2: 50 push %eax80483d3: 68 c8 84 04 08 push $0x80484c880483d8: e8 f7 fe ff ff call 80482d4
// 返回0并退出.80483dd: 31 c0 xor %eax,%eax80483df: c9 leave80483e0: c3 ret

在上面程序中,用likely()代替其中的unlikely(),重新编译,再来看它的汇编代码:

80483b0 
:// 开头80483b0: 55 push %ebp80483b1: 89 e5 mov %esp,%ebp80483b3: 50 push %eax80483b4: 50 push %eax80483b5: 83 e4 f0 and $0xfffffff0,%esp// 调用atoi()80483b8: 8b 45 08 mov 0x8(%ebp),%eax80483bb: 83 ec 1c sub $0x1c,%esp80483be: 8b 48 04 mov 0x4(%eax),%ecx80483c1: 51 push %ecx80483c2: e8 1d ff ff ff call 80482e4
80483c7: 83 c4 10 add $0x10,%esp// --------------------------------------------------// 如果'a' 等于 2 (程序认为很有可能), 则不跳转,继续执行,// 这样就不破坏CPU的指令执行顺序.// 只有当 a != 2 时才会发生跳转, 而这种情况,程序认为是不太可能的.// ---------------------------------------------------80483ca: 83 f8 02 cmp $0x2,%eax80483cd: 75 13 jne 80483e2
// a++ 指令的优化80483cf: b0 03 mov $0x3,%al// 调用printf()80483d1: 52 push %edx80483d2: 52 push %edx80483d3: 50 push %eax80483d4: 68 c8 84 04 08 push $0x80484c880483d9: e8 f6 fe ff ff call 80482d4
// 返回0并退出.80483de: 31 c0 xor %eax,%eax80483e0: c9 leave80483e1: c3 ret

如何使用?
在一个条件判断语句中,当这个条件被认为是非常非常有可能满足时,则使用likely()宏,否则,条件非常非常不可能或很难满足时,则使用unlikely()宏。
参考资料
本文英文原文:http://kernelnewbies.org/FAQ/LikelyUnlikely
更多GCC内置宏或函数,详见:http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

你可能感兴趣的文章
求二叉树第K层的节点数
查看>>
hustoj升级
查看>>
[leetcode]Convert Sorted Array to Binary Search Tree
查看>>
MATLAB求实数绝对值——abs
查看>>
关于yii验证和yii错误处理
查看>>
.NET P2P: Writing Peer-to-Peer Networked Apps with the Microsoft .NET Framework
查看>>
Java 编程下 HashSet 存入相同元素的原理
查看>>
重温C#基础
查看>>
找房 -- 爱合住, ihezhu.com
查看>>
c++ template(6)模板术语
查看>>
10套华丽的 Windows 8 Metro 风格图标【2000+免费图标】
查看>>
Win8:最好用的开始菜单 StartIsBack 2.0.2 Multilingual Crack
查看>>
【20130321】sql server 2005 之后 文件状态变为了7,DEFUNCT(僵死状态)
查看>>
IP address of device using phone as access point
查看>>
3、单机运行环境搭建之 --CentOS-6.5安装配置Tengine
查看>>
.NET平台下几种SOCKET模型的简要性能供参考(转)
查看>>
memcmp和strncmp函数
查看>>
linux下apache字符集问题
查看>>
Deep learning:二十六(Sparse coding简单理解)
查看>>
C#中使用cookies
查看>>