Likely和unlikely 分析

本文详细解析了Linux2.6.38内核中likely()和unlikely()宏函数的原理及使用方法,通过实际代码示例展示了如何利用这些函数优化分支预测,提升程序性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以前我有两个同事写过关于likely()和unlikely()两个宏函数的文档,但是我觉得每个人写的都有自己更为清晰的地方,所以,将二者组合,以及参考网上http://blog.sina.com.cn/s/blog_70dd16910100w018.html得到以下内容,谢谢你们的劳动。

第一, 分析

这两个宏函数的函数含义:

例如在Linux 2.6.38内核include/linux/compiler.h中,这两个宏的定义如下:

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

____builtin_expect是gcc编译器(版本>=2.96)提供给程序员使用,目的是使得程序员可以把分支预测的信息提供给编译器,以降低因为指令跳转带来的分支下降,它的返回值就是它的第一个参数(这个参数必须是整数)传给它的值。

所以在linux2.6.38中,____builtin_expect的返回值就是x的值,所以:

if(likely(value))  等价于 if(value)

if(unlikely(value))等价于 if(value)

这样我们在阅读代码时,就可以把if(likely(value)),if(unlikely(value))看做if(value),便于我们阅读代码。

下面我们来分析__builtin_expect()的机制,在gcc使用手册中有下面一则例子:

if (__builtin_expect (x, 0))

            foo ();

这段程序暗示我们,既然我们期望__builtin_expect (x, 0)返回值为0,那么我们不希望调用foo()函数。


它通过改变汇编指令的执行顺序,来充分利用处理器的流水线。为了达到这样的目的,它直接执行最有可能的分支指令,而尽可能的避免执行跳转指令(jmp),因为jmp指令会刷新CPU的流水线,而影响执行时间。

我们看下面的例子:


第二. 实例分析
//test_builtin_expect.c 
#define LIKELY(x) __builtin_expect(!!(x), 1)
#define UNLIKELY(x) __builtin_expect(!!(x), 0)


int test_likely(int x)
{
    if(LIKELY(x))
    {
        x = 5;
    }
    else
    {
        x = 6;
    }
  
    return x;
}


int test_unlikely(int x)
{
    if(UNLIKELY(x))
    {
        x = 5;
    }
    else
    {
        x = 6;
    }
  
    return x;
}
运行如下命令:
    gcc -fprofile-arcs -O2 -c test_builtin_expect.c 

    objdump -d test_builtin_expect.o


<test_likely>:
00    push     %ebp
01    mov      %esp,%ebp
03    mov      0x8(%ebp),%eax
06    addl     $0x1,0x38
0d    adcl     $0x0,0x3c
14    test     %eax,%eax
16    jz       2d <test_likely+0x2d>//主要看这里。此处的效果是eax不为零时,不需要跳转。即x为真是不跳转。
18    addl     $0x1,0x40
1f    mov      $0x5,%eax
24    adcl     $0x0,0x44
2b    pop      %ebp
2c    ret      
2d    addl     $0x1,0x48
34    mov      $0x6,%eax
39    adcl     $0x0,0x4c
40    pop      %ebp
41    ret      
42    lea      0x0(%esi,%eiz,1),%esi
49    lea      0x0(%edi,%eiz,1),%edi


<test_unlikely>:
50    push     %ebp
51    mov      %esp,%ebp
53    mov      0x8(%ebp),%edx
56    addl     $0x1,0x20
5d    adcl     $0x0,0x24
64    test     %edx,%edx
66    jne      7d <test_unlikely+0x2d>//主要看这里。此处的效果是edx为零时,不需跳转。即x为假时不跳转。
68    addl     $0x1,0x30
6f    mov      $0x6,%eax
74    adcl     $0x0,0x34
7b    pop      %ebp
7c    ret      
7d    addl     $0x1,0x28
84    mov      $0x5,%eax
89    adcl     $0x0,0x2c
90    pop      %ebp
91    ret      
92    lea      0x0(%esi,%eiz,1),%esi
99    lea      0x0(%edi,%eiz,1),%edi

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值