Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3850218
  • 博文数量: 146
  • 博客积分: 3918
  • 博客等级: 少校
  • 技术积分: 8584
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-17 13:52
个人简介

个人微薄: weibo.com/manuscola

文章分类

全部博文(146)

文章存档

2016年(3)

2015年(2)

2014年(5)

2013年(42)

2012年(31)

2011年(58)

2010年(5)

分类: C/C++

2011-08-14 19:07:05

     linux中有likely 和unlikely的宏定义

        #define likely(x) __builtin_expect(!!(x),1)
        #define unlikely(x) __builtin_expect(!!(x),0)
       
        __builtin_expect 宏的含义是通知编译器,对相应的分支预测进行优化。

       我们知道顺序执行的代码CPU比较喜欢,因为不需要跳转。对于存在大量跳转或者说是分支的代码,

CPU比较抓狂,因为CPU一般会提前 取指令 译码,甚至提前计算一些结果。如果分支预测错了,就会丢弃

之前的指令,这样会影响效率。

       如果我们提前知道代码 不同分支的执行概率,我们应该尽量让高概率的分支作为条件转移指令的下一

条指令,这样prefetch 和decode 就会选择条件 转移指令的下一条指令,从而预测成功,这正是我们期望的


       __builtin_expect 就是通知编译器哪个分支出现的概率更高的。

__builtin_expect(!!(x),1)表示,告诉编译器,条件x为真的可能性非常高。

__builtin_expect(!!(x),0)表示,告诉编译器,条件x为假的可能性非常高。



#include #include #include #include #define TIMES (10000000) #define BILLION (1000000000) #define MILLION 1000000 #define likely(x) __builtin_expect(x,1) #define unlikely(x) __builtin_expect(x, 0) int main(int argc,char *argv[]) { int i; int j; int *array = NULL; struct timespec tpstart; struct timespec tpend; unsigned long long timeif; unsigned long long timeif_func; struct timeval tv; if(argc != 2) { printf("usage: ./test times\n"); return -1; } int times = atoi(argv[1]); if(clock_gettime(CLOCK_MONOTONIC,&tpstart)) { fprintf(stderr,"Fail to get start time for NULL\n"); return -1; } for(i = 0;i
(tpend.tv_nsec-tpstart.tv_nsec)/1000; printf("%d BLANK OP 's total time is %llu us\n",times,(timeif)); if(clock_gettime(CLOCK_MONOTONIC,&tpstart)) { fprintf(stderr,"Fail to get start time for branch \n"); return -1; } for(i = 0;i if(likely(j >1)) { array = malloc(1000*sizeof(int)); if(array) free(array); } else { gettimeofday(&tv,NULL); } } if(clock_gettime(CLOCK_MONOTONIC,&tpend)) { fprintf(stderr,"Fail to get start time for branch\n"); return -1; } timeif_func =MILLION*(tpend.tv_sec - tpstart.tv_sec)+
(tpend.tv_nsec-tpstart.tv_nsec)/1000; printf("%d FUNCTION's with right prediction cost time is %llu us\n",
times,(timeif_func)); }


注意粗体部分,j>1的概率几乎是100%,所以我们预测j>1,所以这是正确的预测。我们期待,malloc分支作为条件转移指令的下一条指令,这样,我们就能顺序取指令,无须跳转
看下生成的汇编出来的代码。

gcc -o test_ok test.c -lrt -O2
objdump -d test_ok >objdump_ok

--------------------------objdump_ok---------------------------------------

8048728: e8 1b fe ff ff call 8048548 804872d: 83 f8 01 cmp $0x1,%eax 8048730: 0f 8e ac 00 00 00 jle 80487e2 8048736: c7 04 24 a0 0f 00 00 movl $0xfa0,(%esp) 804873d: e8 f6 fd ff ff call 8048538 8048742: 85 c0 test %eax,%eax 8048744: 74 08 je 804874e 8048746: 89 04 24 mov %eax,(%esp) 8048749: e8 ca fd ff ff call 8048518

.............................................................

80487e2: 8d 44 24 38 lea 0x38(%esp),%eax 80487e6: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp) 80487ed: 00 80487ee: 89 04 24 mov %eax,(%esp) 80487f1: e8 02 fd ff ff call 80484f8 80487f6: e9 53 ff ff ff jmp 804874e

------------------------------------------------------------------------------------
相反,我们如果预测j<1,即将代码中的likely,改成unlikely,那么我们就做出了错误的预测,那么编译器
生成的汇编代码如下。

gcc -o test_err test.c -lrt -O2
objdump -d test_err >objdump_err

-------------------objdump_err-------------------------------------------
8048738: e8 0b fe ff ff call 8048548 804873d: 83 f8 01 cmp $0x1,%eax 8048740: 0f 8f ac 00 00 00 jg 80487f2 8048746: 8d 44 24 38 lea 0x38(%esp),%eax 804874a: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp) 8048751: 00 8048752: 89 04 24 mov %eax,(%esp) 8048755: e8 9e fd ff ff call 80484f8
.................................
80487f2: c7 04 24 a0 0f 00 00 movl $0xfa0,(%esp) 80487f9: e8 3a fd ff ff call 8048538 80487fe: 85 c0 test %eax,%eax 8048800: 0f 84 54 ff ff ff je 804875a 8048806: 89 04 24 mov %eax,(%esp) 8048809: e8 0a fd ff ff call 8048518

------------------------------------------------------------------------------

从上面可以看出,由于我们的预测错误,gettimeofday放到了下一条指令,如果预取指令 译码,甚至执行,会发现,分支预测错了,丢弃之,然后跳转到malloc那条分支。

我们可以预测,由于分支预测出现了错误,test_ok 执行的速度应该比test_err执行的要快。当然,由于CPU还有Branch Target Buffer,这个BTB可以根据上次执行的转移,来预测本次转移。所以下面的两个程序,执行时间差的并不多。


root@libin:~/program/C/function_call# cat do.sh 
 ./test_err 100000000  &
 ./test_ok  100000000  &

root@libin:~/program/C/function_call# ./do.sh 100000000 BLANK OP 's total time is 1 us root@libin:~/program/C/function_call# 100000000 BLANK OP 's total time is 1 us 100000000 FUNCTION's with right prediction cost time is 9472328 us 100000000 FUNCTION's with wrong prediction cost time is 9751683 us


参考文献:1
阅读(4229) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~