转载自:https://my.oschina.net/moooofly/blog/175019

最近看 GLib 的代码遇到这个东东,网上搜索一圈,发现很多人都写过这个,自己今天才研究到,汗颜一下,扫盲一个点,留此记录为证!

首先看一篇最官方的讲解:

====== 
likely() and unlikely()

What are they ? 

In Linux kernel code, one often find calls to likely() and unlikely(), in conditions, like :

bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx);
if (unlikely(!bvl)) {mempool_free(bio, bio_pool);bio = NULL;goto out;
}

In fact, these functions are hints for the compiler that allows it to correctly optimize the branch, by knowing which is the likeliest one. The definitions of these macros, found in include/linux/compiler.h are the following :

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

The GCC documentation explains the role of __builtin_expect() :

-- Built-in Function: long __builtin_expect (long EXP, long C)You may use `__builtin_expect' to provide the compiler with branchprediction information.  In general, you should prefer to useactual profile feedback for this (`-fprofile-arcs'), asprogrammers are notoriously bad at predicting how their programsactually perform.  However, there are applications in which thisdata is hard to collect.The return value is the value of EXP, which should be an integralexpression.  The value of C must be a compile-time constant.  Thesemantics 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 weexpect `x' to be zero.  Since you are limited to integralexpressions for EXP, you should use constructions such asif (__builtin_expect (ptr != NULL, 1))error ();when testing pointer or floating-point values.

How does it optimize things ? 
      It optimizes things by ordering the generated assembly code correctly, to optimize the usage of the processor pipeline. To do so, they arrange the code so that the likeliest branch is executed without performing any jmp instruction (which has the bad effect of flushing the processor pipeline).

To see how it works, let's compile the following simple C user space program with gcc -O2 :

#define likely(x)    __builtin_expect(!!(x), 1)
#define unlikely(x)  __builtin_expect(!!(x), 0)int main(char *argv[], int argc)
{int a;/* Get the value from somewhere GCC can't optimize */a = atoi (argv[1]);if (unlikely (a == 2))a++;elsea--;printf ("%d\n", a);return 0;
}

Now, disassemble the resulting binary using objdump -S (comments added by me) :

080483b0 <main>:// Prologue80483b0:       55                      push   %ebp80483b1:       89 e5                   mov    %esp,%ebp80483b3:       50                      push   %eax80483b4:       50                      push   %eax80483b5:       83 e4 f0                and    $0xfffffff0,%esp//             Call 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 <atoi@plt>80483c7:       83 c4 10                add    $0x10,%esp//             Test the value80483ca:       83 f8 02                cmp    $0x2,%eax//             --------------------------------------------------------//             If 'a' equal to 2 (which is unlikely), then jump,//             otherwise continue directly, without jump, so that it//             doesn't flush the pipeline.//             --------------------------------------------------------80483cd:       74 12                   je     80483e1 <main+0x31>80483cf:       48                      dec    %eax//             Call printf80483d0:       52                      push   %edx80483d1:       52                      push   %edx80483d2:       50                      push   %eax80483d3:       68 c8 84 04 08          push   $0x80484c880483d8:       e8 f7 fe ff ff          call   80482d4 <printf@plt>//             Return 0 and go out.80483dd:       31 c0                   xor    %eax,%eax80483df:       c9                      leave80483e0:       c3                      ret

Now, in the previous program, replace the unlikely() by a likely(), recompile it, and disassemble it again (again, comments added by me) :

080483b0 <main>://             Prologue80483b0:       55                      push   %ebp80483b1:       89 e5                   mov    %esp,%ebp80483b3:       50                      push   %eax80483b4:       50                      push   %eax80483b5:       83 e4 f0                and    $0xfffffff0,%esp//             Call 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 <atoi@plt>80483c7:       83 c4 10                add    $0x10,%esp//             --------------------------------------------------//             If 'a' equal 2 (which is likely), we will continue//             without branching, so without flusing the pipeline. The//             jump only occurs when a != 2, which is unlikely.//             ---------------------------------------------------80483ca:       83 f8 02                cmp    $0x2,%eax80483cd:       75 13                   jne    80483e2 <main+0x32>//             Here the a++ incrementation has been optimized by gcc80483cf:       b0 03                   mov    $0x3,%al//             Call 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 <printf@plt>//             Return 0 and go out.80483de:       31 c0                   xor    %eax,%eax80483e0:       c9                      leave80483e1:       c3                      ret

How should I use it ? 
      You should use it only in cases when the likeliest branch is very very very likely, or when the unlikeliest branch is very very very unlikely.

======

看完最权威的,下面看下“民间”的说法:

====== 
likely,unlikely宏与GCC内建函数__builtin_expect()

在 GCC 手册中对 __builtin_expect() 的描述是这样的:

由于大部分程序员在分支预测方面做得很糟糕,所以 GCC 提供了这个内建函数来帮助程序员处理分支预测,优化程序。其第一个参数 exp 为一个整型表达式,这个内建函数的返回值也是这个 exp ,而 c 为一个编译期常量。这个函数的语义是:你期望 exp 表达式的值等于常量 c ,从而 GCC 为你优化程序,将符合这个条件的分支放在合适的地方。一般情况下,你也许会更喜欢使用 gcc 的一个参数 '-fprofile-arcs' 来收集程序运行的关于执行流程和分支走向的实际反馈信息。 
      因为这个程序只提供了整型表达式,所以如果你要优化其他类型的表达式,可以采用指针的形式。

likely 和 unlikely 是 gcc 扩展的跟处理器相关的宏:

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

现在处理器都是流水线的,有些里面有多个逻辑运算单元,系统可以提前取多条指令进行并行处理,但遇到跳转时,则需要重新取指令,这相对于不用重新去指令就降低了速度。  
      所以就引入了 likely 和 unlikely ,目的是增加条件分支预测的准确性,cpu 会提前装载后面的指令,遇到条件转移指令时会提前预测并装载某个分 支的指令。unlikely 表示你可以确认该条件是极少发生的,相反 likely 表示该条件多数情况下会发生。编译器会产生相应的代码来优化 cpu 执行效率。

因此程序员在编写代码时可以根据判断条件发生的概率来优化处理器的取指操作。

例如:

int x, y;
if(unlikely(x > 0)) y = 1;
else y = -1;

上面的代码中 gcc 编译的指令会预先读取 y = -1 这条指令,这适合 x 的值大于 0 的概率比较小的情况。  如果 x 的值在大部分情况下是大于 0 的,就应该用 likely(x > 0),这样编译出的指令是预先读取 y = 1 这条指令了。这样系统在运行时就会减少重新取指了。

====== 
内核中的 likely() 与 unlikely()

首先要明确:

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

__builtin_expect() 是 GCC (version >= 2.96)提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。

__builtin_expect((x),1) 表示 x 的值为真的可能性更大; 
__builtin_expect((x),0) 表示 x 的值为假的可能性更大。

也就是说,使用 likely() ,执行 if 后面的语句 的机会更大,使用 unlikely(),执行 else 后面的语句的机会更大。通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着起面的代码,从而减少指令跳转带来的性能上的下降。

======

看完一圈别 人写的东西,自己也要输出点干货,列举 GLib-2.35.4 中  gmacros.h 代码如下:

/** The G_LIKELY and G_UNLIKELY macros let the programmer give hints to * the compiler about the expected result of an expression. Some compilers* can use this information for optimizations.** The _G_BOOLEAN_EXPR macro is intended to trigger a gcc warning when* putting assignments in g_return_if_fail ().  */
#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
#define _G_BOOLEAN_EXPR(expr)                   \G_GNUC_EXTENSION ({                            \int _g_boolean_var_;                         \if (expr)                                    \_g_boolean_var_ = 1;                      \else                                         \_g_boolean_var_ = 0;                      \_g_boolean_var_;                             \
})
// 为条件判断提供程序员期望的结果-- 用于编译器优化
#define G_LIKELY(expr) (__builtin_expect (_G_BOOLEAN_EXPR(expr), 1))
#define G_UNLIKELY(expr) (__builtin_expect (_G_BOOLEAN_EXPR(expr), 0))
#else
#define G_LIKELY(expr) (expr)
#define G_UNLIKELY(expr) (expr)
#endif

由上可以看出, GLib 中使用  _G_BOOLEAN_EXPR(expr) 代替了 !!(expr) 。但功能上是一样的。

__builtin_expect 解惑相关推荐

  1. 话里话外:新顾问答疑解惑对话大公开

    提问人: malven.mao 解惑人:aaron.sun malven.mao: 对于某个项目,要列pdca, 但是我不确定某些问题可能会做多久, 会做成怎么样, 在这种情况下, 我该怎么来列计划? ...

  2. 院长齐聚,答疑解惑 | 清华-青岛数据科学研究院“院长接待日”成功举办

    2017年12月14日下午,清华-青岛数据科学研究院(以下简称:数据院)"院长接待日"活动在双清大厦四层成功举行.院长俞士纶.副院长王建民和执行副院长韩亦舜与来自校内不同院系对大数 ...

  3. SAE上传web应用(包括使用数据库)教程详解及问题解惑

    2019独角兽企业重金招聘Python工程师标准>>> 转自:http://blog.csdn.net/baiyuliang2013/article/details/24725995 ...

  4. 新浪项目-------小知识点答疑解惑

    1.IOS解惑(1)之@property(nonatomic,getter=isOn) BOOL on;中的getter解惑 //如果这个property是 BOOL on,那么Objc默认创建的 s ...

  5. C# 浅拷贝与深拷贝区别 解惑篇

    问题起源: 昨天被同事问到一个浅拷贝与深拷贝区别的问题,说实在的,记得在学校时在书在看过相关概念区别. 只是,那时的在校生,又有几个能对书本上那写的尽量让鬼都看不懂知识能清晰的理解呢. 工作后虽然也有 ...

  6. Android解惑 - 为什么要用Fragment.setArguments(Bundle bundle)来传递参数

     Android解惑 - 为什么要用Fragment.setArguments(Bundle bundle)来传递参数 时间 2014-04-19 23:05:55 CSDN博客 原文  http ...

  7. (0046) iOS开发之View的frame和bounds之解惑

    iOS view的frame和bounds之解惑 frame: 该view在父view坐标系统中的位置和大小.(参照点是,父亲的坐标系统) bounds:该view在本地坐标系统中的位置和大小.(参照 ...

  8. 《Visual C++ 开发从入门到精通》——1.4 技术解惑

    本节书摘来自异步社区出版社<Visual C++ 开发从入门到精通>一书中的第1章,第1.4节,作者: 王东华 , 李樱,更多章节内容可以访问云栖社区"异步社区"公众号 ...

  9. 大众EA211djs和css的区别,宣传上却说捷达VS5用的发动机是EA211,但这款车的参数配置栏发动机型号写的却是DJS,谁能为我解惑吗?(只有斯柯达的柯米克的发动机型号写得才是EA211)...

    捷达vs5宣传上却说捷达VS5用的发动机是EA211,但这款车的参数配置栏发动机型号写的却是DJS,谁能为我解惑吗?(只有斯柯达的柯米克的发动机型号写得才是EA211)问题 下面小编为大家整理4个捷达 ...

最新文章

  1. DllMain already defined in dllmain.obj错误
  2. JUC系列(十一) | Java 8 CompletableFuture 异步编程
  3. QT学习:获取文件详细信息
  4. SAP UI5 的初始化过程
  5. MapReduce学习笔记(4)
  6. 全国计算机等级考试汇编,2011年3月汇编全国计算机等级考试(南开100题三级网络技术上机试题汇编)...
  7. 比赛总结——atcoder beginner contest 109
  8. linux三剑客应用到工作中,LINUX 三剑客老大(AWK) 日常工作总结(示例代码)
  9. breadweb控制台下载_路由器刷breed web控制台助手通用版
  10. 如何将多个图片批量转换成pdf文件?
  11. Vue项目调用扫一扫功能 亲测好用!!!
  12. paper的经验和会议排名
  13. TRANSFORMER TRANSDUCER: A STREAMABLE SPEECH RECOGNITION MODELWITH TRANSFORMER ENCODERS AND RNN-T
  14. python财务预算分析_财码Python管理会计小实验—滚动预算vs定期预算
  15. 硬盘提示设备未就绪要如何办啊
  16. 【win11】安装WIN11启用TPM2.0的华硕主板M10H使用英特尔CPU设置PTT解决方案全记录
  17. positio有哪些属性?position的属性如何使用
  18. 跟着 NC 学作图 | 多组散点图+配对连线+差异分析
  19. 【每周CV论文推荐】基于GAN的对抗攻击,适合阅读那些文章入门?
  20. 大数据导论期末大作业

热门文章

  1. overflow问题
  2. js实现常用排序算法
  3. 浏览器url特殊字符自动转换的编解码问题:
  4. 如何用AR引擎技术, 5步优雅实现物体识别和跟踪
  5. paddlepaddle框架——波士顿房价预测模型(附原始数据)
  6. 基于java SSM框架+微信小程序实现电子书城阅读器演示【附项目源码+论文说明】分享
  7. 操作系统-父亲放苹果
  8. 小程序开发.mpvue.项目构建与运行
  9. MYSQL存储过程进行写读操作(for循环)
  10. 【社区图书馆】记:读完《网络工程师的Python之路:网络运维自动化实战(第2版)》读后感【书评】