1.概述
c/c++编程中会看到likeyly和unlikey的使用,其定义如下:
#define likely(x) __builtin_expect(!!(x),1)
#define unlikely(x) __builtin_expect(!!(x),0)
_builtin_expect 这个指令是 gcc 引入的。该函数作用是允许程序员将最有可能执行的分支告诉编译器,来辅助系统进行分支预测。当正确地使用了__builtin_expect后,编译器在编译过程中会根据程序员的指令,将可能性更大的代码紧跟着前面的代码,从而减少指令跳转带来的性能上的下降。
2.示例
#include <stdio.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
int main(int argc, char *argv[])
{
int n;
n = atoi(argv[1]);
if (likely(n > 10)){
n = n + 2;
} else {
n = n - 2;
}
printf("%d\n", n);
return 0;
}
~
编译:
/arm-linux-androideabi-gcc -pie -fPIE -g -fprofile-arcs -O2 -o likely likely.c
反汇编代码:
fd0: e350000a cmp r0, #10
fd4: da00000f ble 1018 <main+0x6c>
fd8: e1c420d8 ldrd r2, [r4, #8]
fdc: e2801002 add r1, r0, #2
fe0: e2922001 adds r2, r2, #1
小于10 ble跳转到1018代码处理,否则继续执行,即n+2指紧接者执行的。
把上面代码linkey替换成unlikey的反汇编代码:
fcc: ebffff57 bl d30 <atoi@plt>
fd0: e350000a cmp r0, #10
fd4: ca00000f bgt 1018 <main+0x6c>
fd8: e1c421d0 ldrd r2, [r4, #16]
fdc: e2401002 sub r1, r0, #2
可以看到此时大于10发生了跳转(前面是小于10),n-2是紧接着执行的。
3.原理
likely优化性能的原因主要有两个方面:
- 根据cpu的流水线的执行原理,likely帮助编译器将大概率执行的代码紧挨着当前的pc地址,避免分支预测失败的惩罚。
- 利用计算机的局部性原理,可以有利于将大概率执行的代码一次性加载到cache line。