首先,无论在何处,当我们遇到宏likely和宏unlikely时,都要明确一点:

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

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

    也就是说 ,当value值为真时执行if分支,为假时执行else分支,从阅读和理解代码的角度来看,是一样的!!!

以下为这两个宏的一般定义:

#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)
    而__builtin_expect() 是 GCC (version >= 2.96)提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。__builtin_expect()在GCC的官方文档中解释如下(可跳过):

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

也就是说,GCC的内建方法会判断 EXP == C 是否成立,成立则将if分支中的执行语句紧跟放在汇编跳转指令之后,否则将else分支中的执行语句紧跟汇编跳转指令之后。如下例子所示:

点击(此处)折叠或打开

  1. //test.c
  2. #define likely(x) __builtin_expect(!!(x),1)
  3. #define unlikely(x) __builtin_expect(!!(x),0)
  4. int test_unlikely(int x)
  5. {
  6. if(unlikely(x == 2))
  7. {
  8. x++;
  9. }
  10. else
  11. {
  12. x--;
  13. }
  14. return x;
  15. }
  16. int test_likely(int x)
  17. {
  18. if(likely(x == 2))
  19. {
  20. x++;
  21. }
  22. else
  23. {
  24. x--;
  25. }
  26. return x;
  27. }

编译并导出目标文件的汇编表示:

gcc -fprofile-arcs -O2 -c test.c

objdump -d test.o

得到如下汇编:

test.o:     file format elf32-i386

Disassembly of section .text:

00000000 <test_likely>:
   0: 55                                 push   %ebp
   1: 83 05 00 00 00 00 01       addl   $0x1,0x0
   8: 89 e5                            mov    %esp,%ebp
   a: 83 15 04 00 00 00 00       adcl   $0x0,0x4
  11: 83 7d 08 02                   cmpl   $0x2,0x8(%ebp)                   //留意这里!!!判断 x == 2
  15: 75 15                            jne    2c <test_likely+0x2c>               //跳转指令jne!!!x != 2 时才跳转!
  17: 83 05 08 00 00 00 01       addl   $0x1,0x8                          //if分支代码 x++
  1e: b8 03 00 00 00               mov    $0x3,%eax
  23: 83 15 0c 00 00 00 00       adcl   $0x0,0xc
  2a: 5d                                pop    %ebp
  2b: c3                                ret   
  2c: 8b 45 08                       mov    0x8(%ebp),%eax                   //跳转到这里,else分支代码 x--
  2f: 5d                                pop    %ebp
  30: 83 e8 01                       sub    $0x1,%eax
  33: 83 05 10 00 00 00 01      addl   $0x1,0x10
  3a: 83 15 14 00 00 00 00      adcl   $0x0,0x14
  41: c3                               ret   
  42: 8d b4 26 00 00 00 00      lea    0x0(%esi),%esi
  49: 8d bc 27 00 00 00 00      lea    0x0(%edi),%edi

00000050 <test_unlikely>:
  50: 55                               push   %ebp
  51: 89 e5                           mov    %esp,%ebp
  53: 8b 45 08                       mov    0x8(%ebp),%eax
  56: 83 05 18 00 00 00 01      addl   $0x1,0x18
  5d: 83 15 1c 00 00 00 00      adcl   $0x0,0x1c
  64: 83 f8 02                       cmp    $0x2,%eax                          //留意这里!!!判断 x == 2
  67: 74 13                           je     7c <test_unlikely+0x2c>           //跳转指令je!!!x == 2 时就跳转!
  69: 83 e8 01                       sub    $0x1,%eax                          //else分支代码 x--
  6c: 83 05 28 00 00 00 01      addl   $0x1,0x28
  73: 83 15 2c 00 00 00 00      adcl   $0x0,0x2c
  7a: 5d                               pop    %ebp
  7b: c3                               ret   
  7c: 83 05 20 00 00 00 01      addl   $0x1,0x20                       //跳转到这里,if分支代码 x++
  83: b0 03                           mov    $0x3,%al
  85: 83 15 24 00 00 00 00      adcl   $0x0,0x24
  8c: 5d                               pop    %ebp
  8d: c3                               ret   
  8e: 66 90                           xchg   %ax,%ax

00000090 <_GLOBAL__I_0_test_unlikely>:
  90: 55                    push   %ebp
  91: 89 e5                 mov    %esp,%ebp
  93: 83 ec 08              sub    $0x8,%esp
  96: c7 04 24 00 00 00 00  movl   $0x0,(%esp)
  9d: e8 fc ff ff ff        call   9e <_GLOBAL__I_0_test_unlikely+0xe>
  a2: c9                    leave 
  a3: c3                    ret

   注意:likely和unlikely所生成的跳转指令是不同的,分别是jne和je!!!

如上述例子分析所示,宏likely和宏unlikely唯一的作用就是选择“将if分支还是else分支放在跳转指令之后,从而优化程序的执行效率”。 因为likely(EXP)代表条件表达式EXP很可能成立,而unlikely(EXP)代表条件表达式EXP很可能不成立,当程序员清楚EXP表达式 多数情况成立(不成立)时,就可使用likely(unlikely),使if分支(else分支)紧跟跳转指令其后,从而在大多数情况下不用执行跳转指 令,避开跳转指令所带来的开销,从而达到优化的目的。

最新文章

  1. OpenGL材质和光照(转)part1
  2. 神经网络通用近似定理
  3. 中国首个火星探测器天问一号发射成功!
  4. ajax判断密码是否一致,jquery.validate ajax方式验证密码是否正确
  5. 人质困境:多个人的囚徒困境(博弈论的诡计)
  6. java ssm框架详解_Java的SSM框架怎样才算真正掌握?
  7. java.lang.AbstractMethodError: org.apache.xerces.dom.DocumentImpl.setXmlVersion
  8. gbdt和xgboost中feature importance的获取
  9. 统计相关系数r与r2的区别_什么是相关系数? 统计解释中的r值
  10. js里apply用法
  11. AdventureWorks2008R2安装过程可能会遇到的一些问题及解决方案
  12. SpringBoot整合J2Cache
  13. mysql删除视图sql语句_删除视图的sql语句是什么
  14. JS 实现列表移动(JQuery实现)
  15. 用Git在阿里云下克隆时出现“Please make sure you have the correct access rights and the repository exists”错误
  16. java环境变量classpath的作用_JAVA环境变量中 classpath、path、JAVA_HOME的作用
  17. HFDS命令行操作(开发重点)
  18. 最新Python 实现自动登录抖音(京东),实现滑块自动滑过
  19. Win7下硬盘响声大的解决办法
  20. 基于51单片机的鸡舍智能环境控制有害气体检测无线WiFi通信proteus仿真原理图PCB

热门文章

  1. Graphite安装
  2. linux下载东西显示No package nginx available.无库可用
  3. win7系统无法激活问题
  4. STM32 CRH和CRL设置方向
  5. 图文笔记,带你走进《未来简史》(16-20)
  6. picker-view-column自定义picker
  7. 将 Cpar 文件导入 2019 版的 Carsim 后,无法打开 video+plot 是什么问题?
  8. Android开发中EventLog分析
  9. 网络通信TCP/UDP
  10. IOS开发使用@IBInspectable给控件添加额外属性