如果你读过Linux代码的非驱动部分,一定会见过likely和unlikely,今天说说它们。
浅尝
它们实际上是2个宏,__builtin_expect
是gcc编译优化的build-in function,尽管我没有在gcc官方网页上找到说明,不过实践上确实好用。字面的意思就是likely中的值为非零的概率大,unlikely中的值为零的概率大,编译器要根据这个概率优化指令。内核代码执行的重复度很高,所以每一条指令的优化都有意义。
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
代码演示
#include <stdio.h>
//这个函数的内容请忽略,写复杂点只是为了不被优化
void func(int i){
volatile int x = i;
x++;
i = x + 100;
printf("%d", i);
}
int func0(int value){
int ret;
value = !!value;
if (__builtin_expect(value, 1)){
ret = 100;
ret = ret + ret;
ret = ret + ret;
func(ret);
}
else{
ret = 50;
ret = 50 + ret;
ret = 50 - ret;
func(ret);
}
return ret;
}
int func1(int value){
int ret;
value = !!value;
if (__builtin_expect(value, 0)){
ret = 100;
ret = ret + ret;
ret = ret + ret;
func(ret);
}
else{
ret = 50;
ret = 50 + ret;
ret = 50 - ret;
func(ret);
}
return ret;
}
编译
编译指令是gcc -c test.c -marm -O2 -o test.o
,要加-O2
,因为这东西是用来优化的,只有在优化编译时起作用。
反汇编
注意func0
和func1
中的跳转条件,不跳转意味着流水线的顺畅运行,即效率较高。
00000000 <func>:
0: e52de004 push {lr} ; (str lr, [sp, #-4]!)
...
34: e49df004 pop {pc} ; (ldr pc, [sp], #4)
00000038 <func0>:
38: e3500000 cmp r0, #0
3c: e92d4008 push {r3, lr}
40: 0a000003 beq 54 <func+0x54> ;非0时不跳转
44: e3a00e19 mov r0, #400 ; 0x190
48: ebfffffe bl 0 <func>
4c: e3a00e19 mov r0, #400 ; 0x190
50: e8bd8008 pop {r3, pc}
54: e3e00031 mvn r0, #49 ; 0x31
58: ebfffffe bl 0 <func>
5c: e3e00031 mvn r0, #49 ; 0x31
60: e8bd8008 pop {r3, pc}
00000064 <func1>:
64: e3500000 cmp r0, #0
68: e92d4008 push {r3, lr}
6c: 1a000003 bne 80 <func+0x80> ;0时不跳转
70: e3e00031 mvn r0, #49 ; 0x31
74: ebfffffe bl 0 <func>
78: e3e00031 mvn r0, #49 ; 0x31
7c: e8bd8008 pop {r3, pc}
80: e3a00e19 mov r0, #400 ; 0x190
84: ebfffffe bl 0 <func>
88: e3a00e19 mov r0, #400 ; 0x190
8c: e8bd8008 pop {r3, pc}