曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 3436|回复: 0
打印 上一主题 下一主题

likely() | unlikely()

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34395
跳转到指定楼层
楼主
发表于 2011-2-26 23:52:16 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
在内核代码中常见到两个宏 likely() 和 unlikly() ,它们定义在 include/linux/compiler.h 中:

#define likely(x)    __builtin_expect(!!(x), 1)
#define unlikely(x)    __builtin_expect(!!(x), 0)
__builtin_expect 是 gcc 的内置函数,它的原型为 long __builtin_expect (long exp, long c) ,第 1 个参数可以是个整数,也可以是个布尔型表达式,函数的返回值为 exp 。使用 __builtin_expect 可以为编译器提供跳转预测信息,关于跳转预测相关内容可参考:http://www.groad.net/bbs/read.php?tid-1455-fpage-3.htmlhttp://www.groad.net/bbs/read.php?tid-1456-fpage-3.html


该函数的意思是,期望 " exp == c " 。因此,


__builtin_expect(!!(x), 1) 表示 !!(x) 为真的可能性很大,如果 !!(x) 确实为真,那么整个函数的返回值为真。
__builtin_expect(!!(x), 0) 表示 !!(x) 为假的可能性很大,如果 !!(x) 确实为假,那么整个函数的返回值为假。


注意,上面的可能性是程序员所“认为”的,但实际情况不一定就是真或假,然而编译器会按照你所认为的 “可能性” 去安排跳转预测。


下面通过测试程序来验证:
#include <stdio.h>

#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

void test_like (int x)
{

        if (unlikely(x))
                x = x + 8;

        else
                x = x + 10;


        printf ("x = %d\n", x);
}

int main(void)
{

        test_like (10);
        return (0);
}
编译
gcc -O2 builtin_expect.c -o builtin_expect
反汇编
08048430 <test_like>:
8048430:       55                      push   %ebp
8048431:       89 e5                   mov    %esp,%ebp
8048433:       83 ec 18                sub    $0x18,%esp
8048436:       8b 45 08                mov    0x8(%ebp),%eax
8048439:       85 c0                   test   %eax,%eax
804843b:       75 17                   jne    8048454 <test_like+0x24>
804843d:       b8 0a 00 00 00          mov    $0xa,%eax       /* x = x + 10; */
8048442:       89 44 24 04             mov    %eax,0x4(%esp)
8048446:       c7 04 24 50 85 04 08    movl   $0x8048550,(%esp)
804844d:       e8 02 ff ff ff          call   8048354 <printf@plt>
8048452:       c9                      leave  

8048453:       c3                      ret   
8048454:       83 c0 08                add    $0x8,%eax             /* x = x + 8 */
8048457:       eb e9                   jmp    8048442 <test_like+0x12>
8048459:       8d b4 26 00 00 00 00    lea    0x0(%esi,%eiz,1),%esi

将上面程序中的 test_like(10) 中的 10 改为 0 再看反汇编
08048430 <test_like>:
8048430:       55                      push   %ebp
8048431:       89 e5                   mov    %esp,%ebp
8048433:       83 ec 18                sub    $0x18,%esp
8048436:       8b 45 08                mov    0x8(%ebp),%eax
8048439:       85 c0                   test   %eax,%eax
804843b:       75 17                   jne    8048454 <test_like+0x24>
804843d:       b8 0a 00 00 00          mov    $0xa,%eax
8048442:       89 44 24 04             mov    %eax,0x4(%esp)
8048446:       c7 04 24 50 85 04 08    movl   $0x8048550,(%esp)
804844d:       e8 02 ff ff ff          call   8048354 <printf@plt>
8048452:       c9                      leave  
8048453:       c3                      ret   
8048454:       83 c0 08                add    $0x8,%eax
8048457:       eb e9                   jmp    8048442 <test_like+0x12>
8048459:       8d b4 26 00 00 00 00    lea    0x0(%esi,%eiz,1),%esi

从上面可以看到两个反汇编的内容一样,也就是说不管传递的参数是真或是假,程序跳转预测的安排都只是根据 __builtin_expect 来安排。
因为 unlikely() 是条件很可能为假,那么也就是要执行 x = x + 10 这条语句,所以编译器不安排跳转。从指令缓存的角度来看,跳转很可能是跳转到缓存之外的,这样会减慢程序的执行速度。


同样可以测试 likely() 的情况。


另外需要注意一点区别,unlikely() 用的是 jne 指令,而 likely() 用的是 je 指令。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-5-4 01:07 , Processed in 0.084470 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表