今天看GCD 源码,有个宏定义中有 __builtin_expect命令
#define _safe_cast_to_long(x) \
({ _Static_assert(sizeof(typeof(x)) <= sizeof(long), \
"__builtin_expect doesn't support types wider than long"); \
(long)(x); })
#define fastpath(x) ((typeof(x))__builtin_expect(_safe_cast_to_long(x), ~0l))
#define slowpath(x) ((typeof(x))__builtin_expect(_safe_cast_to_long(x), 0l))
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
这个命令干么的呢?
这个指令是gcc引入的,作用是允许程序员将最有可能执行的分支告诉编译器。这个指令的写法为:__builtin_expect(EXP, N)。
意思是:EXP==N的概率很大。
首先要明确:
if(likely(value)) //等价于 if(value) 只不过value 可能为真的可能性更大。
if(unlikely(value)) //也等价于 if(value) 只不过value可能为假 的可能性更大
__builtin_expect() 是 GCC (version >= 2.96)提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。
__builtin_expect((x),1)表示 x 的值为真的可能性更大
__builtin_expect((x),0)表示 x 的值为假的可能性更大。
也就是说,使用likely(),执行 if 后面的语句的机会更大,使用 unlikely(),执行 else 后面的语句的机会更大。通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着起面的代码,从而减少指令跳转带来的性能上的下降。
///测试代码main.m
#define LIKELY(x) __builtin_expect(!!(x), 1)
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
int test_likely(int x)
{
if(LIKELY(x))
{
x = 5;
}
else
{
x = 6;
}
return x;
}
int test_unlikely(int x)
{
if(UNLIKELY(x))
{
x = 5;
}
else
{
x = 6;
}
return x;
}
gcc -fprofile-arcs -O2 -c main.m
objdump -d main.o
获取到的汇编
_test_likely:
0: 55 pushq %rbp
1: 48 89 e5 movq %rsp, %rbp
4: 31 c0 xorl %eax, %eax
6: 85 ff testl %edi, %edi
8: 0f 94 c0 sete %al
b: 48 8d 0d 00 00 00 00 leaq (%rip), %rcx
12: 48 ff 04 c1 incq (%rcx,%rax,8)
16: 85 ff testl %edi, %edi
18: 74 15 je 21 <_test_likely+0x2F> ///这里是je
1a: 48 ff 05 10 00 00 00 incq 16(%rip)
21: b8 05 00 00 00 movl $5, %eax
26: 48 ff 05 20 00 00 00 incq 32(%rip)
2d: 5d popq %rbp
2e: c3 retq
2f: 48 ff 05 18 00 00 00 incq 24(%rip)
36: b8 06 00 00 00 movl $6, %eax
3b: eb e9 jmp -23 <_test_likely+0x26>
3d: 0f 1f 00 nopl (%rax)
_test_unlikely:
40: 55 pushq %rbp
41: 48 89 e5 movq %rsp, %rbp
44: 31 c0 xorl %eax, %eax
46: 85 ff testl %edi, %edi
48: 0f 94 c0 sete %al
4b: 48 8d 0d 00 00 00 00 leaq (%rip), %rcx
52: 48 ff 04 c1 incq (%rcx,%rax,8)
56: 85 ff testl %edi, %edi
58: 75 15 jne 21 <_test_unlikely+0x2F> ///这里是jne
5a: 48 ff 05 18 00 00 00 incq 24(%rip)
61: b8 06 00 00 00 movl $6, %eax
66: 48 ff 05 20 00 00 00 incq 32(%rip)
6d: 5d popq %rbp
6e: c3 retq
6f: 48 ff 05 10 00 00 00 incq 16(%rip)
76: b8 05 00 00 00 movl $5, %eax
7b: eb e9 jmp -23 <_test_unlikely+0x26>
7d: 0f 1f 00 nopl (%rax)
要是去掉LIKELY 和UNLIKELY
汇编结果如下
_test_likely:
0: 55 pushq %rbp
1: 48 89 e5 movq %rsp, %rbp
4: 31 c0 xorl %eax, %eax
6: 85 ff testl %edi, %edi
8: 0f 94 c0 sete %al
b: 48 8d 0d 00 00 00 00 leaq (%rip), %rcx
12: 48 ff 04 c1 incq (%rcx,%rax,8)
16: 85 ff testl %edi, %edi
18: 74 0e je 14 <_test_likely+0x28>
1a: 48 ff 05 10 00 00 00 incq 16(%rip)
21: b8 05 00 00 00 movl $5, %eax
26: eb 0c jmp 12 <_test_likely+0x34>
28: 48 ff 05 18 00 00 00 incq 24(%rip)
2f: b8 06 00 00 00 movl $6, %eax
34: 48 ff 05 20 00 00 00 incq 32(%rip)
3b: 5d popq %rbp
3c: c3 retq
3d: 0f 1f 00 nopl (%rax)
_test_unlikely:
40: 55 pushq %rbp
41: 48 89 e5 movq %rsp, %rbp
44: 31 c0 xorl %eax, %eax
46: 85 ff testl %edi, %edi
48: 0f 94 c0 sete %al
4b: 48 8d 0d 00 00 00 00 leaq (%rip), %rcx
52: 48 ff 04 c1 incq (%rcx,%rax,8)
56: 85 ff testl %edi, %edi
58: 74 0e je 14 <_test_unlikely+0x28> ///unlikely 也变成了je,意思是执行if后面的语句
5a: 48 ff 05 10 00 00 00 incq 16(%rip)
61: b8 05 00 00 00 movl $5, %eax
66: eb 0c jmp 12 <_test_unlikely+0x34>
68: 48 ff 05 18 00 00 00 incq 24(%rip)
6f: b8 06 00 00 00 movl $6, %eax
74: 48 ff 05 20 00 00 00 incq 32(%rip)
7b: 5d popq %rbp
7c: c3 retq
7d: 0f 1f 00 nopl (%rax)