本文讲的likely()和unlikely()两个宏,在linux内核代码和一些应用中可常见到它们的身影。实质上,这两个宏是关于GCC编译器内置宏__builtin_expect的使用。
顾名思义,likely()指“很有可能”之意,而unlikely()指“不太可能”之意。那么,在实际应用中,它们代表什么?又是怎么使用的呢?下面是一篇外文翻译(加上了本人的一些理解),给出了详细答案。
likely()和unlikely()
对于linux内核代码,在条件判断语句中经常看到likely()和unlikely()的调用,如下代码所示:
1 |
bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx); |
3 |
mempool_free(bio, bio_pool); |
在这里,调用likely()或unlikely()告诉编译器这个条件很有可能或者不太有可能发生,好让编译器对这个条件判断进行正确地优化。这两个宏在include/linux/compiler.h文件中可以找到:
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, ))
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, ))
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) |
04 |
int main(char *argv[], int argc) |
08 |
/* 获取输入参数值(编译器不能进行优化) */ |
11 |
if (unlikely (a == 2)) |
使用objdump -S反汇编,查看它的汇编代码。
04 |
80483b1: 89 e5 mov %esp,%ebp |
07 |
80483b5: 83 e4 f0 and $0xfffffff0,%esp |
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 |
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> |
27 |
80483d3: 68 c8 84 04 08 push $0x80484c8 |
28 |
80483d8: e8 f7 fe ff ff call 80482d4 <printf@plt> |
30 |
80483dd: 31 c0 xor %eax,%eax |
在上面程序中,用likely()代替其中的unlikely(),重新编译,再来看它的汇编代码:
04 |
80483b1: 89 e5 mov %esp,%ebp |
07 |
80483b5: 83 e4 f0 and $0xfffffff0,%esp |
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 |
13 |
80483c2: e8 1d ff ff ff call 80482e4 <atoi@plt> |
14 |
80483c7: 83 c4 10 add $0x10,%esp |
15 |
// -------------------------------------------------- |
16 |
// 如果'a' 等于 2 (程序认为很有可能), 则不跳转,继续执行, |
18 |
// 只有当 a != 2 时才会发生跳转, 而这种情况,程序认为是不太可能的. |
19 |
// --------------------------------------------------- |
20 |
80483ca: 83 f8 02 cmp $0x2,%eax |
21 |
80483cd: 75 13 jne 80483e2 <main+0x32> |
23 |
80483cf: b0 03 mov $0x3,%al |
28 |
80483d4: 68 c8 84 04 08 push $0x80484c8 |
29 |
80483d9: e8 f6 fe ff ff call 80482d4 <printf@plt> |
31 |
80483de: 31 c0 xor %eax,%eax |
如何使用?
在一个条件判断语句中,当这个条件被认为是非常非常有可能满足时,则使用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任意类型的东东了.
- [转] GCC __builtin_expect的作用
http://blog.csdn.net/shuimuniao/article/details/8017971 将流水线引入cpu,可以提高cpu的效率.更简单的说,让cpu可以预先取出下一条指令,可 ...
- __builtin_expect
今天在看内核代码中看到: #define likely(x) __builtin_expect(!!(x), 1)#define unlikely(x) __builtin_expect(!!(x), ...
- __builtin_expect — 分支预测优化
1.引言 在很多源码如Linux内核.Glib等,我们都能看到likely()和unlikely()这两个宏,通常这两个宏定义是下面这样的形式. #define likely(x) __builtin ...
- GCC __builtin_expect的作用
https://blog.csdn.net/shuimuniao/article/details/8017971 #define LIKELY(x) __builtin_expect(!!(x), 1 ...
- likely,unlikely宏与GCC内建函数__builtin_expect()
在 GCC 手册中对 __builtin_expect() 的描述是这样的: 由于大部分程序员在分支预测方面做得很糟糕,所以 GCC 提供了这个内建函数来帮助程序员处理分支预测,优化程序.其第一个参数 ...
- GCC的分支预测优化__builtin_expect
智能指针笔记 GCC的原子操作函数 将流水线引入cpu,可以提高cpu的效率.更简单的说,让cpu可以预先取出下一条指令,可以提供cpu的效率.如下图所示: 取指令 执行指令 输出结果 取指令 执行 ...
- 【原】AFNetworking源码阅读(六)
[原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AF ...
- [开发笔记]GCC 分支预测优化
#define likely(x) __builtin_expect(!!(x),1)#define unlikely(x) __builtin_expect(!!(x),0) 用于优化在做分支判断的 ...
- gcc/linux内核中likely、unlikely和__attribute__(section(""))属性
查看linux内核源码,你会发现有很多if (likely(""))...及if (unlikely(""))...语句,这些语句其实是编译器的一种优化方式,具 ...
随机推荐
- The Definitive Guide To Django 2 学习笔记(四) 动态URLs
前面的例子中,虽然时间是动态可变的,但它的URL却是静态的(/time/).很多时候,URL也是需要动态改变,然后展示出不通的内容来.现在我们就来创建一个可以动态改变URL的例子. 如果URLconf ...
- JAVA转化Unicode编码
package yyl.example.basic.codec; import java.util.Locale; import java.util.regex.Matcher; import jav ...
- www--摘录图解TCP/IP
万维网,www,world wide web,也称web.将互联网中的信息以超文本的形式展现的系统.可以显示www信息的客户端软件叫做web浏览器. www内容 www定义了3个重要的概念,它们分别是 ...
- 用HTML5/CSS3/JS开发Android/IOS应用框架大全
现在人人都想成为安卓/IOS应用开发工程师.其实,安卓/IOS应用可以用很多种语言来实现.由于我们前端开发工程师,对HTML5/CSS/JavaScript的网络编程已经相当熟悉了.所以,今天大家将会 ...
- datatable.js 服务端分页+fixColumns列固定
前言 记得还是15年的时候,工作需要,独自写后台管理系统..记得那时候,最让我头疼的不是后台逻辑,而是数据的列表展示. 列很多的时候,页面显示问题;分页样式问题;表格样式问题;数据加载...很多细节的 ...
- Matlab之合并音频
程序功能: 1.读入wav下的所有音频 2.每个音频截取前0.6秒 3.合并每个音频 clear all; cd = 'wav'; waveFiles = dir(fullfile(cd,'*.wav ...
- Ubuntu apt-get update错误解决
用apt-get命令安装开发软件非常方便,但由于各种原因,经常链接不上软件源,于是需要使用sudo apt-get update命令来更新软件源. 而屋漏偏逢连夜雨,这时候更新命令也罢工,出现各种错误 ...
- 从客户端检测到有潜在危险的Request.Form 值”错误提示
http://www.cnblogs.com/UouHt/archive/2008/10/30/1322697.html asp.net开发中,经常遇到“从客户端检测到有潜在危险的Request.Fo ...
- 房间WIFI信号不好怎么办?——无线路由桥接(WDS)
背景 1.无线路由在客厅,房间的WIFI信号很差,只有1-2格,虽说是100M的网速,但是从客厅到房间要经过3道墙!电脑的无线接收功能一般都很一般,网速不好理所当然. 2.弄一根网线从客厅连接起来很傻 ...
- Laravel5.1 搭建博客 --展示简单的首页
今天起开始搭建博客,把之前学的东西运用下. 1 创建 配置项目 1.1 创建项目 composer create-project laravel/laravel blog 5.1.1 1.2 配置数据 ...