likely && unlikely in GCC
在linux内核源码或一些比较成熟的c语言架构源码中,我们常会见到类似下面的代码:
if (unlikely(!packet)) {
return res_failed;
}
// OR
if (likely(packet->type = HTTP)) {
do_something();
}
有的地方可能会用大写,LIKELY() / UNLIKELY(),意思一样。
然后我们看一下unlikely和likely的定义,大部分都是类似如下的宏定义:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0) GCC 中用的是 _G_BOOLEAN_EXPR(expr) 来代替 !!(expr), 意思一样,都是把expr或x转成相应布尔变量。
两个定义无一例外调用了一个内置函数 __builtin_expect(bool expr, int x)。
先解释一下: LIKELY 和 UNLIKELY 不会对原expr的布尔值产生任何影响,也就是说只要expr == true, LIKELY(expr) 与 UNLIKELY(expr) 都为 true。他们起的只是编译器优化作用。
我们先测试一段代码:
/**
* @author Lhfcws
* @file test__builtin_expect.c
* @time 2013-07-22
**/ #define LIKELY(x) __builtin_expect(!!(x), 1)
#define UNLIKELY(x) __builtin_expect(!!(x), 0) int test_likely(int x) {
if(LIKELY(x))
x = 0x00;
else
x = 0xff; return x;
} int test_unlikely(int x) {
if(UNLIKELY(x))
x = 0x00;
else
x = 0xff; return x;
} int test_justif(int x) {
if(x)
x = 0x00;
else
x = 0xff; return x;
}
可见,三个函数唯一的区别就是 if (x) 那里。
我们执行一下命令编译和反汇编(要用 __builtin_expect 的话 -fprofile-arcs 必须加):
gcc -fprofile-arcs -c test__builtin_expect.c -o test__builtin_expect.o
objdump -d test__builtin_expect > builtin.asm
此时打开生成的asm文件,就可以看到上面代码由gcc编译生成的汇编代码。我们截取那三个函数的汇编源码查看。
<test_likely>:
: push %ebp
: e5 mov %esp,%ebp
: 7d cmpl $0x0,0x8(%ebp)
: 0f c0 setne %al
a: 0f b6 c0 movzbl %al,%eax
d: c0 test %eax,%eax
f: je 1a <test_likely+0x1a>
: c7 movl $0x0,0x8(%ebp)
: eb jmp 3d <test_likely+0x3d>
1a: c7 ff movl $0xff,0x8(%ebp)
: a1 mov 0x20,%eax
: 8b mov 0x24,%edx
2c: c0 add $0x1,%eax
2f: d2 adc $0x0,%edx
: a3 mov %eax,0x20
: mov %edx,0x24
3d: 8b 4d mov 0x8(%ebp),%ecx
: a1 mov 0x28,%eax
: 8b 2c mov 0x2c,%edx
4b: c0 add $0x1,%eax
4e: d2 adc $0x0,%edx
: a3 mov %eax,0x28
: 2c mov %edx,0x2c
5c: c8 mov %ecx,%eax
5e: 5d pop %ebp
5f: c3 ret <test_unlikely>:
: push %ebp
: e5 mov %esp,%ebp
: 7d cmpl $0x0,0x8(%ebp)
: 0f c0 setne %al
6a: 0f b6 c0 movzbl %al,%eax
6d: c0 test %eax,%eax
6f: je 7a <test_unlikely+0x1a>
: c7 movl $0x0,0x8(%ebp)
: eb jmp 9d <test_unlikely+0x3d>
7a: c7 ff movl $0xff,0x8(%ebp)
: a1 mov 0x10,%eax
: 8b mov 0x14,%edx
8c: c0 add $0x1,%eax
8f: d2 adc $0x0,%edx
: a3 mov %eax,0x10
: mov %edx,0x14
9d: 8b 4d mov 0x8(%ebp),%ecx
a0: a1 mov 0x18,%eax
a5: 8b 1c mov 0x1c,%edx
ab: c0 add $0x1,%eax
ae: d2 adc $0x0,%edx
b1: a3 mov %eax,0x18
b6: 1c mov %edx,0x1c
bc: c8 mov %ecx,%eax
be: 5d pop %ebp
bf: c3 ret 000000c0 <test_justif>:
c0: push %ebp
c1: e5 mov %esp,%ebp
c3: 7d cmpl $0x0,0x8(%ebp)
c7: je d2 <test_justif+0x12>
c9: c7 movl $0x0,0x8(%ebp)
d0: eb jmp f5 <test_justif+0x35>
d2: c7 ff movl $0xff,0x8(%ebp)
d9: a1 mov 0x0,%eax
de: 8b mov 0x4,%edx
e4: c0 add $0x1,%eax
e7: d2 adc $0x0,%edx
ea: a3 mov %eax,0x0
ef: mov %edx,0x4
f5: 8b 4d mov 0x8(%ebp),%ecx
f8: a1 mov 0x8,%eax
fd: 8b 0c mov 0xc,%edx
: c0 add $0x1,%eax
: d2 adc $0x0,%edx
: a3 mov %eax,0x8
10e: 0c mov %edx,0xc
: c8 mov %ecx,%eax
: 5d pop %ebp
: c3 ret
如上,我们看到,貌似test_likely 和 test_unlikely 没什么区别, test_justif就是少了setne al开始的三行代码而已(实际上是执行__builtin_expect(!!(x), 1)的代码)。
其实这证明了一件事: LIKELY 和 UNLIKELY 的调用不会影响最终结果,实际两者的结果是一样的。
我们之前提到他们起的作用是优化,因此我们编译的时候加上优化指令。
gcc -O2 -fprofile-arcs -c test__builtin_expect.c -o test__builtin_expect.o
objdump -d test__builtin_expect > builtin_O2.asm
得到汇编:
<test_likely>:
: ec sub $0x4,%esp
: 8b mov 0x8(%esp),%eax
: addl $0x1,0x0
e: adcl $0x0,0x4
: c0 test %eax,%eax
: je 1f <test_likely+0x1f>
: c0 xor %eax,%eax
1b: c4 add $0x4,%esp
1e: c3 ret
1f: addl $0x1,0x8
: b8 ff mov $0xff,%eax
2b: 0c adcl $0x0,0xc
: eb e7 jmp 1b <test_likely+0x1b>
: 8d b6 lea 0x0(%esi),%esi
3a: 8d bf lea 0x0(%edi),%edi <test_unlikely>:
: ec sub $0x4,%esp
: 8b mov 0x8(%esp),%edx
: addl $0x1,0x10
4e: adcl $0x0,0x14
: d2 test %edx,%edx
: jne <test_unlikely+0x30>
: addl $0x1,0x18
: b8 ff mov $0xff,%eax
: 1c adcl $0x0,0x1c
6c: c4 add $0x4,%esp
6f: c3 ret
: c0 xor %eax,%eax
: eb f8 jmp 6c <test_unlikely+0x2c>
: 8d b6 lea 0x0(%esi),%esi
7a: 8d bf lea 0x0(%edi),%edi <test_justif>:
: ec sub $0x4,%esp
: 8b 4c mov 0x8(%esp),%ecx
: addl $0x1,0x20
8e: adcl $0x0,0x24
: c0 xor %eax,%eax
: c9 test %ecx,%ecx
: jne ab <test_justif+0x2b>
9b: addl $0x1,0x28
a2: b0 ff mov $0xff,%al
a4: 2c adcl $0x0,0x2c
ab: c4 add $0x4,%esp
ae: c3 ret
现在三个函数就有很明显的不同了。
留意一下每个函数其中的三行代码:
je ... / jne ... ; 跳转
xor %eap, %eap ; 其实是 mov 0x00, %eap,改用 xor 是编译器自己的优化。结果等价。
mov 0xff, %eap ; x = 0xff
可以看到,likely版本和unlikely版本最大的区别是跳转的不同。
likely版本编译器会认为执行 x == true 的可能性比较大,因此将 x == false 的情况作为分支,减少跳转开销。
同理,unlikely版本编译器会认为执行 x == false 的可能性比较大,因此将 x == true 的情况作为分支,减少跳转开销。
总结:
likely 和 unlikely 的使用实际上是为了分支优化,不影响结果,据传,众多程序员平常很少注意分支优化情况,因此gcc有了这个选项。。。
unlikely 一般适用于(但不仅限于)一些错误检查,比如本文开头示例。likely适用于主分支场景,即根据期望大部分情况都会执行的场景。
likely && unlikely in GCC的更多相关文章
- VSCode调试go语言出现:exec: "gcc": executable file not found in %PATH%
1.问题描述 由于安装VS15 Preview 5,搞的系统由重新安装一次:在用vscdoe编译go语言时,出现以下问题: # odbcexec: "gcc": executabl ...
- GCC学习(1)之MinGW使用
GCC学习(1)之MinGW使用 因为后续打算分享一些有关GCC的使用心得的文章,就把此篇当作一个小预热,依此来了解下使用GNU工具链(gcc.gdb.make等)在脱离IDE的情况下如何开发以及涉及 ...
- 使用 GCC 和 GNU Binutils 编写能在 x86 实模式运行的 16 位代码
不可否认,这次的标题有点长.之所以把标题写得这么详细,主要是为了搜索引擎能够准确地把确实需要了解 GCC 生成 16 位实模式代码方法的朋友带到我的博客.先说一下背景,编写能在 x86 实模式下运行的 ...
- [异常解决] How to build a gcc toolchain for nRF51 on linux (very detailed!!!)
1.Install gcc-arm-none-eabi https://devzone.nordicsemi.com/tutorials/7/This link shows that developm ...
- CentOS 6.6 升级GCC G++ (当前最新版本为v6.1.0) (完整)
---恢复内容开始--- CentOS 6.6 升级GCC G++ (当前最新GCC/G++版本为v6.1.0) 没有便捷方式, yum update.... yum install 或者 添加y ...
- GCC 预处理、编译、汇编、链接..
1简介 GCC 的意思也只是 GNU C Compiler 而已.经过了这么多年的发展,GCC 已经不仅仅能支持 C 语言:它现在还支持 Ada 语言.C++ 语言.Java 语言.Objective ...
- 用gcc进行程序的编译
在Linux系统上,一个档案能不能被执行看的是有没有可执行的那个权限(x),不过,Linux系统上真正认识的可执行文件其实是二进制文件(binary program),例如/usr/bin/passw ...
- gcc/linux内核中likely、unlikely和__attribute__(section(""))属性
查看linux内核源码,你会发现有很多if (likely(""))...及if (unlikely(""))...语句,这些语句其实是编译器的一种优化方式,具 ...
- Ubuntu 14.04 LTS 下升级 gcc 到 gcc-4.9、gcc-5 版本
如果没记错的话,阿里云ECS上的Ubuntu也是LTS版本. 如果还在使用较旧版本的Ubuntu,或者是Ubuntu LTS,那么我们是很难体验新版gcc的.怎么办呢? 我们或许可以自己去编译用旧版本 ...
- 低版本GCC程序向高版本移植的兼容性问题
将低版本gcc编译过的程序移植到高版本GCC时, 可能会出现一些兼容性问题. 原因是, 为了适应新的标准,一些旧的语法规则被废弃了. 关于这方面的一些具体资料可从该处查询. 这里只是自己遇到的其中一个 ...
随机推荐
- 如何在网页中添加“QQ交流”
今天在撸码时,想到这个问题,有些网页中会有诸如,那么如何在网页添加"QQ交谈"? 第一步.登录QQ: 第二步.打开网页:QQ推广,启用QQ通讯组件: 第三步.选择组件样式,设置提示 ...
- 利用js刷新页面方法
1,reload 方法,该方法强迫浏览器刷新当前页面. location.reload(force) 如果该方法没有规定参数,或者参数是 false,它就会用 HTTP 头 If-Modified-S ...
- hibernate中数据库方言
在配置hibernate.cfg.xml时需指定使用数据库的方言: 例: <property name="dialect">org.hibernate.dialect. ...
- PHP 图片处理工具类(添加水印与生成缩略图)
=================ImageTool.class.php================= <?php class ImageTool { private $imagePath; ...
- 2016年11月28日--ADO.Net 增、删、改、查
数据访问 对应命名空间:System.Data.SqlClient; SqlConnection:连接对象SqlCommand:命令对象SqlDataReader:读取器对象 CommandText: ...
- 微信录音接口的调用以及amr文件转码MP3文件的实现
最近实现录音功能,主要涉及到录音的上传和下载,以及转码问题.微信,QQ默认的的音频文件是amr格式的,而播放器却不识别amr格式的音频,必须尽行转码.amr文件分为两种,一种是通用的amr格式,这种文 ...
- WORDPRESS点击标题或图片无法链接到文章页面
在设置出更改固定连接设置
- Maven下载依赖项的源代码(source code)和Javadoc
Maven 默认只下载依赖项本身的 jar 文件,不下载源代码和 Javadoc.如此固然工程的体积是最小的,但在开发者不熟悉依赖的对象时,需要查找源代码中的方法定义和说明. 这时我们需要使用一条 M ...
- 前端 js 实现简单 表单提交
1. 登录页 验证用户身份,登录成功之后等待一定秒数,跳转到操作页面 <html> <head> <title>Login.html</title> & ...
- 使用EmBitz开发STM32项目的环境配置
一.EmBitz软件获取与安装 1.EmBitz软件的获取 EmBitz原名Em::Blocks,是基于Code::Blocks开发的,面向嵌入式的C/C++集成开发环境.支持J-Link和ST-Li ...