- 论坛徽章:
- 0
|
本帖最后由 tbag-tang 于 2010-10-23 21:09 编辑
在内核源码中经常出现以下两个宏:likely(x) 和 unlikely(x)
其定义如下:
# define likely(x) __builtin_expect(!!(x), 1)
# define unlikely(x) __builtin_expect(!!(x), 0)
我先解释下这里的gcc内建函数 __builtin_expect
在/usr/include/gmp-i386.h中有如下说明.
/* __builtin_expect is in gcc 3.0, and not in 2.95. */
__builtin_expect(!!(x), 1)
这里表示条件为真的可能性比较大
__builtin_expect(!!(x), 0)
这里表示条件为假的可能性比较大
注:如果x为0,则!!(x)还是0; 如果x不为0,则!!(x)为1.
真的是这样吗? 带着这个疑问接着往下看。
测试程序如下:
1) __builtin_expect(!!(x), 0)
// test.c
#include <stdio.h>
void ketty(int va)
{
if (__builtin_expect(!!(va), 0)) {
va = va + 8;
}
else {
va = va + 98;
}
printf("%d\n", va);
}
int main(void)
{
ketty(1);
return 0;
}
编译及反汇编过程如下:
tbag@:~/test$ gcc -O2 test.c -o unlikely
tbag@:~/test$ objdump -D unlikely > unlike
tbag@:~/test$ vi unlike
08048410 <ketty>:
8048410: 55 push %ebp
8048411: 89 e5 mov %esp,%ebp
8048413: 83 ec 18 sub $0x18,%esp
8048416: 8b 45 08 mov 0x8(%ebp),%eax
------------------------------------------------------------
8048419: 85 c0 test %eax,%eax
804841b: 75 1c jne 8048439 <ketty+0x29>
804841d: b0 62 mov $0x62,%al
------------------------------------------------------------
804841f: 89 44 24 08 mov %eax,0x8(%esp)
8048423: c7 44 24 04 20 85 04 movl $0x8048520,0x4(%esp)
804842a: 08
804842b: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048432: e8 f5 fe ff ff call 804832c <__printf_chk@plt>
8048437: c9 leave
8048438: c3 ret
------------------------------------------------------------
8048439: 83 c0 08 add $0x8,%eax
------------------------------------------------------------
804843c: eb e1 jmp 804841f <ketty+0xf>
804843e: 66 90 xchg %ax,%ax
TEST 指令对两个操作数作按位与运算,将影响的标记记在标志位中,
供查询使用,而源目操作数均不改变。
note: 这里使用的是jne(知道这点很重要),紧跟其后的是else语句块里的代码
2) __builtin_expect(!!(x), 1)
#include <stdio.h>
void ketty(int va)
{
if (__builtin_expect(!!(va), 1)) {
va = va + 8;
}
else {
va = va + 98;
}
printf("%d\n", va);
}
int main(void)
{
ketty(1);
return 0;
}
编译及反汇编过程如下:
tbag@:~/test$ gcc -O2 test.c -o likely
tbag@:~/test$ objdump -D likely > like
tbag@:~/test$ vi like
08048410 <ketty>:
8048410: 55 push %ebp
8048411: 89 e5 mov %esp,%ebp
8048413: 83 ec 18 sub $0x18,%esp
8048416: 8b 45 08 mov 0x8(%ebp),%eax
------------------------------------------------------------
8048419: 85 c0 test %eax,%eax
804841b: 74 1d je 804843a <ketty+0x2a>
804841d: 83 c0 08 add $0x8,%eax
------------------------------------------------------------
8048420: 89 44 24 08 mov %eax,0x8(%esp)
8048424: c7 44 24 04 20 85 04 movl $0x8048520,0x4(%esp)
804842b: 08
804842c: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048433: e8 f4 fe ff ff call 804832c <__printf_chk@plt>
8048438: c9 leave
8048439: c3 ret
------------------------------------------------------------
804843a: b0 62 mov $0x62,%al
------------------------------------------------------------
804843c: eb e2 jmp 8048420 <ketty+0x10>
804843e: 66 90 xchg %ax,%ax
note: 这里使用的是je,紧跟其后的是if语句块里的代码。
分析:
1.__builtin_expect(!!(x), 0)
a) 使用的是jne跳转指令。
b) 通过反汇编可以看出, 编译器将if语句块里面的代码放在else语句块之后。
也就是说,编译器将“预期"的小概率事件放在代码段的后面。
2.__builtin_expect(!!(x), 1)
.) 跟__builtin_expect(!!(x), 0) 刚好相反。
这样做有什么优化吗?
我来说下我的个人观点:
.Cache
根据局部性原理,CPU总是将最近访问主存的数据存储到cache中. 这样的话,大概率事件放在代码前面,命中
的机率就越大,也就是说将小概率事件放在代码段后面,就越能提高Cache的使用效率。
以下是摘自ARM System Developer's Guide: Designing and System Software的一段对Cache的描述。
cache is a small, fast array of memory placed between the processor and main memory. It
is a holding buffer that stores portions of recently referenced system memory. The processor
uses cache memory in preference to system memory whenever possible to increase average
system performance.
以上是我的一些认识。 |
|