Sir zach

专注于算法,AI, Android以及音视频领域 欢迎关注我的最新博客: zachliu.cn

导航

likely 和 unlikely 的使用

Posted on 2014-03-07 21:33  SirZach  阅读(760)  评论(0编辑  收藏  举报

内核版本3.13.6中, likely() 与 unlikely()定义位于/include/linux/compiler.h中,

具体定义如下:

#define likely(x) __builtin_expect(!!(x), 1)

#define unlikely(x) __builtin_expect(!!(x), 0)

 ===========================================

 # ifndef likely

# define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
# endif

# ifndef unlikely
# define unlikely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 0))
# endif

 


__builtin_expect是gcc(版本>=2.96)中提供的一个预处理命令,有利于代码优化。gcc(version 4.4.0)具体定义如下:
long __builtin_expect (long exp, long c) [Built-in Function]

官方解释:

You may use __builtin_expect to provide the compiler with branch prediction information. In general, you should prefer to use actual profile feedback for this (-fprofile-arcs), as programmers are notoriously bad at predicting how their programs actually perform. However, there are applications in which this data is hard to collect.

The return value is the value of exp, which should be an integral expression. The semantics of the built-in are that it is expected that exp == c. For example:

          if (__builtin_expect (x, 0))
            foo ();

indicates that we do not expect to call foo, since we expect x to be zero. Since you are limited to integral expressions for exp, you should use constructions such as

          if (__builtin_expect (ptr != NULL, 1))
            foo (*ptr);

when testing pointer or floating-point values.

http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

它的意思是:我们可以使用这个函数人为告诉编绎器一些分支预测信息“exp==c” 是“很可能发生的”。

#define likely(x) __builtin_expect(!!(x), 1)也就是说明x==1是“经常发生的”或是“很可能发生的”。
使用likely ,执行if后面语句的可能性大些,编译器将if{}是的内容编译到前面, 使用unlikely ,执行else后面语句的可能性大些,编译器将else{}里的内容编译到前面。这样有利于cpu预取,提高预取指令的正确率,因而可提高效率。

正好在看书的时候有这么一段代码:

 /* 减少preempt_count,如果该返回值为0, 将导致自动激活下半部 */

void local_bh_enable(void)
2 {
3     struct thread_info *t = current_thread_info()
4     t->preempt_count -= SOFTIRQ_OFFSET;
5  
6     /* 当preempt_count为0且存在挂起的下半部时,则执行 */    
7     if (unlikely(!t->preempt_count && softirq_pending(smp_processor_id())))
8          do_softirq();  
9 } 

 

此代码用于激活本地处理器的软中断和tasklet, 是此功能的c语言近似描述, 其汇编实现位于<asm/softirq.h>中。 

 

编译过程中,会将if后面{}里的内容编译到前面,else 后面{}里的内容编译到后面。若将likely换成unlikely 则正好相反。

 
总之,likely与unlikely互换或不用都不会影响程序的正确性。但可能会影响程序的效率。
 
所以: 
if(likely(foo))  //认为foo通常为1
if(unlikely(foo)) //认为foo通常为0
 

而!!(x)操作会将x的值转化为1或0, 这也是为什么不写成__builtin_expect(x, 1)的原因。