likely(x)与unlikely(x) __builtin_expect

本文讲的likely()和unlikely()两个宏,在linux内核代码和一些应用中可常见到它们的身影。实质上,这两个宏是关于GCC编译器内置宏__builtin_expect的使用。

顾名思义,likely()指“很有可能”之意,而unlikely()指“不太可能”之意。那么,在实际应用中,它们代表什么?又是怎么使用的呢?下面是一篇外文翻译(加上了本人的一些理解),给出了详细答案。

likely()和unlikely()

对于linux内核代码,在条件判断语句中经常看到likely()和unlikely()的调用,如下代码所示:

1 bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx);
2 if (unlikely(!bvl)) {
3   mempool_free(bio, bio_pool);
4   bio = NULL;
5   goto out;
6 }

在这里,调用likely()或unlikely()告诉编译器这个条件很有可能或者不太有可能发生,好让编译器对这个条件判断进行正确地优化。这两个宏在include/linux/compiler.h文件中可以找到:

view source

1 #define likely(x)       __builtin_expect(!!(x), 1)
2 #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进行编译。

01 #define likely(x)    __builtin_expect(!!(x), 1)
02 #define unlikely(x)  __builtin_expect(!!(x), 0)
03  
04 int main(char *argv[], int argc)
05 {
06    int a;
07  
08    /* 获取输入参数值(编译器不能进行优化) */
09    a = atoi (argv[1]);
10  
11    if (unlikely (a == 2))
12       a++;
13    else
14       a--;
15  
16    printf ("%d\n", a);
17  
18    return 0;
19 }

使用objdump -S反汇编,查看它的汇编代码。

01 080483b0 <main>:
02  //             开头
03  80483b0:       55                      push   %ebp
04  80483b1:       89 e5                   mov    %esp,%ebp
05  80483b3:       50                      push   %eax
06  80483b4:       50                      push   %eax
07  80483b5:       83 e4 f0                and    $0xfffffff0,%esp
08  //             调用atoi()
09  80483b8:       8b 45 08                mov    0x8(%ebp),%eax
10  80483bb:       83 ec 1c                sub    $0x1c,%esp
11  80483be:       8b 48 04                mov    0x4(%eax),%ecx
12  80483c1:       51                      push   %ecx
13  80483c2:       e8 1d ff ff ff          call   80482e4 <atoi@plt>
14  80483c7:       83 c4 10                add    $0x10,%esp
15  //             把输入值与2进行比较,即执行:“a == 2”
16  80483ca:       83 f8 02                cmp    $0x2,%eax
17  //             --------------------------------------------------------
18  //             如果‘a‘ 等于 2 (程序里面认为不太可能), 则跳转,
19  //             否则继续执行, 从而不破坏CPU的指令执行顺序.
20  //             --------------------------------------------------------
21  80483cd:       74 12                   je     80483e1 <main+0x31>
22  80483cf:       48                      dec    %eax
23  //             调用printf
24  80483d0:       52                      push   %edx
25  80483d1:       52                      push   %edx
26  80483d2:       50                      push   %eax
27  80483d3:       68 c8 84 04 08          push   $0x80484c8
28  80483d8:       e8 f7 fe ff ff          call   80482d4 <printf@plt>
29  //             返回0并退出.
30  80483dd:       31 c0                   xor    %eax,%eax
31  80483df:       c9                      leave
32  80483e0:       c3                      ret

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

01 080483b0 <main>:
02  //             开头
03  80483b0:       55                      push   %ebp
04  80483b1:       89 e5                   mov    %esp,%ebp
05  80483b3:       50                      push   %eax
06  80483b4:       50                      push   %eax
07  80483b5:       83 e4 f0                and    $0xfffffff0,%esp
08  //             调用atoi()
09  80483b8:       8b 45 08                mov    0x8(%ebp),%eax
10  80483bb:       83 ec 1c                sub    $0x1c,%esp
11  80483be:       8b 48 04                mov    0x4(%eax),%ecx
12  80483c1:       51                      push   %ecx
13  80483c2:       e8 1d ff ff ff          call   80482e4 <atoi@plt>
14  80483c7:       83 c4 10                add    $0x10,%esp
15  //             --------------------------------------------------
16  //             如果‘a‘ 等于 2 (程序认为很有可能), 则不跳转,继续执行,
17  //             这样就不破坏CPU的指令执行顺序.
18  //             只有当 a != 2 时才会发生跳转, 而这种情况,程序认为是不太可能的.
19  //             ---------------------------------------------------
20  80483ca:       83 f8 02                cmp    $0x2,%eax
21  80483cd:       75 13                   jne    80483e2 <main+0x32>
22  //             a++ 指令的优化
23  80483cf:       b0 03                   mov    $0x3,%al
24  //             调用printf()
25  80483d1:       52                      push   %edx
26  80483d2:       52                      push   %edx
27  80483d3:       50                      push   %eax
28  80483d4:       68 c8 84 04 08          push   $0x80484c8
29  80483d9:       e8 f6 fe ff ff          call   80482d4 <printf@plt>
30  //             返回0并退出.
31  80483de:       31 c0                   xor    %eax,%eax
32  80483e0:       c9                      leave
33  80483e1:       c3                      ret

如何使用?

在一个条件判断语句中,当这个条件被认为是非常非常有可能满足时,则使用likely()宏,否则,条件非常非常不可能或很难满足时,则使用unlikely()宏。

参考资料

本文英文原文:http://kernelnewbies.org/FAQ/LikelyUnlikely

更多GCC内置宏或函数,详见:http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

likely(x) 其实就是期望x的值为1
   if(unlikely(x)){
           foo();
      }
来测试条件的话,我们就期望foo()函数执行,所以该宏的名字用likely也就是可能来表示。
  unlikely(x)望表达式x的值为0,从而如果我们用
   if(unlikely(x)){
           bar();
      }
来测试条件的话,我们就不期望bar()函数执行,所以该宏的名字用unlikely也就是不太可能来表示。
   上面这两条语句都是x为1的时候执行。其实也就一句话:if() 语句你照用, 跟以前一样, 只是 如果你觉得if()是1 的可能性非常大的时候, 就在表达式的外面加一个likely() , 如果可能性非常小(比如几率非常小),就用unlikely() 包裹上。 你也可以完全不用likely(),unlikely().
  这里注意下:macro的定义x有括号.这应该也是c的基础了,不过我们一般还是会疏忽的.这就说明x可以用表达式了,于是likely也就可以test任意类型的东东了.

时间: 2025-01-01 06:48:03

likely(x)与unlikely(x) __builtin_expect的相关文章

[转] GCC __builtin_expect的作用

http://blog.csdn.net/shuimuniao/article/details/8017971 将流水线引入cpu,可以提高cpu的效率.更简单的说,让cpu可以预先取出下一条指令,可以提供cpu的效率.如下图所示: +-------------------------------- |取指令 | 执行指令 | 输出结果 +-------------------------------- |             | 取指令     | 执行 +----------------

__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 functi

malloc和free的内存到底有多大?——GNU glib库

大家应该都比较熟悉这一点:malloc分配的内存一定大于用户指定的大小!而且很多人也问过这样的问题:到底大多少?以及实际上malloc到底分配了多少? 我们知道这个大小一定在某个"神奇"地方记录着,但是就像自己的"思维"一样,你确无法感知!不过,这是错觉,只是我们习惯了只使用,而没有深入剖析源码,在这里我将揭开这个面纱,去掉其透明化! 声明:源码基于GNU glib库的2.7版本的malloc目录下相关文件 再声明:不同的C库实现方式不一定一样,这里是glib库,如

AFNetworking2.0源码解析&lt;三&gt;

本篇说说安全相关的AFSecurityPolicy模块,AFSecurityPolicy用于验证HTTPS请求的证书,先来看看HTTPS的原理和证书相关的几个问题. HTTPS HTTPS连接建立过程大致是,客户端和服务端建立一个连接,服务端返回一个证书,客户端里存有各个受信任的证书机构根证书,用这些根证书对服务端 返回的证书进行验证,经验证如果证书是可信任的,就生成一个pre-master  secret,用这个证书的公钥加密后发送给服务端,服务端用私钥解密后得到pre-master secr

libev 宏展开

想看源码,宏太多,看着累,宏展开,再看,功力时间不够,先放下 放上宏展开后的代码. libev4.20 展开方示为 ./configure 修改makefile文件,字符串 替换CC为 CPP 注意要把基础的CC定义保留 make mv ev.o ev.c mv event.o event.c make clean 修改makefile文件,字符串替换CPP->CC 注意要把基础的CPP定义保留 宏展开后#号注释的代码,可以修改CPP的定义 加个参数 ev.c,event.c 及为展开后的代码

Linux 内核中 likely 与 unlikely 的宏定义解析

在 2.6 内核中,随处可以见到 likely() 和 unlikely() 的身影,那么为什么要用它们?它们之间有什么区别? 首先要明确: if(likely(value)) 等价于 if(value) if(unlikely(value)) 也等价于 if(value) 也就是说 likely() 和 unlikely() 从阅读和理解代码的角度来看,是一样的!!! #define likely(x) __builtin_expect((x),1) #define unlikely(x) _

pthread_mutex_lock/unlock(1)

我们来考察下pthread中锁的实现. 首先看下初始化宏:PTHREAD_MUTEX_INITIALIZER. # define PTHREAD_MUTEX_INITIALIZER { { 0, 0, 0, 0, 0, __PTHREAD_SPINS, { 0, 0 } } } /* Data structures for mutex handling. The structure of the attribute type is not exposed on purpose. *//*删减了3

(转)CPU Cache与内存对齐

转自:http://blog.csdn.net/zhang_shuai_2011/article/details/38119657 原文如下: 一. CacheCache一般来说,需要关心以下几个方面1)Cache hierarchyCache的层次,一般有L1, L2, L3 (L是level的意思)的cache.通常来说L1,L2是集成 在CPU里面的(可以称之为On-chip cache),而L3是放在CPU外面(可以称之为Off-chip cache).当然这个不是绝对的,不同CPU的做

深入了解GCD

首先提出一些问题: dispatch_async 函数如何实现,分发到主队列和全局队列有什么区别,一定会新建线程执行任务么? dispatch_sync 函数如何实现,为什么说 GCD 死锁是队列导致的而不是线程,死锁不是操作系统的概念么? 信号量是如何实现的,有哪些使用场景? dispatch_group 的等待与通知.dispatch_once 如何实现? dispatch_source 用来做定时器如何实现,有什么优点和用途? dispatch_suspend 和 dispatch_res