likely 和 unlikely 的分析

likely 和 unlikely在内核(2.6.8)中的定义:

------------------------------------------------------------------include/linux/complier-gcc2.h
/* Somewhere in the middle of the GCC 2.96 development cycle, we implemented
   a mechanism by which the user can annotate likely branch directions and
   expect the blocks to be reordered appropriately.  Define __builtin_expect
   to nothing for earlier compilers.  */
#include

#if __GNUC_MINOR__ < 96
# define __builtin_expect(x, expected_value) (x)
#endif

------------------------------------------------------------------include/linux/complier-gcc2.h

------------------------------------------------------------------include/linux/complier.h
/*
 * Generic compiler-dependent macros required for kernel
 * build go below this comment. Actual compiler/compiler version
 * specific implementations come from the above header files
 */

#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
------------------------------------------------------------------include/linux/complier.h


likely 和 unlikely在gcc中的定义:
-------------------------------------------------------------------info gcc 来自gcc的帮助
- Built-in Function: long __builtin_expect (long EXP, long C)
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 value of C must be a compile-time constant. The
semantics of the built-in are that it is expected that EXP == C.
For example:

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

would indicate 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))
error ();

when testing pointer or floating-point values.
-------------------------------------------------------------------info gcc 来自gcc的帮助


如下面代码:
ptr = malloc(size);
if ( likely(ptr==NULL) ) /* 我们不希望ptr为空*/
{
    printf("malloc memory failed!");
    return -1;
}

也可以写成:
if ( unlikely(ptr==NULL))                    
{
    printf("malloc memory failed!");
    return -1;
}
如果你希望做分支预测,则用likely,否则用unlikely.
很明显,应该对流程最常走的路径用分支预测,而相反的不要分支预测.


请大家注意likely和unlikely语义上没有区别,如果 ptr的值为NULL,则打印malloc memory failed! 返回 -1;
不同的是,从反编译的汇编语句中可以看出,对于有预读指令的cpu来说,进行了一定的优化!

上面的代码中,多数情况ptr非空,所以不期望进入printf()语句中。 unlikely比likely更优化!

在 2.6 内核中,随处可以见到 likely() 和 unlikely() 的身影,那么为什么要用它们?它们之间有什么区别?

首先要明确:<?XML:NAMESPACE PREFIX = O />

            if(likely(value)) 等价于 if(value)

            if(unlikely(value)) 也等价于 if(value)

也就是说 likely() unlikely() 从阅读和理解代码的角度来看,是一样的!!!


这两个宏在内核中定义如下:

#define  likely(x)       __builtin_expect((x),1)
#define  unlikely(x)     __builtin_expect((x),0)


__builtin_expect() 是 GCC (version >= 2.96)提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化, 以减少指令跳转带来的性能下降。

__builtin_expect((x),1) 表示 x 的值为真的可能性更大;
__builtin_expect((x),0) 表示 x 的值为假的可能性更大。

也就是说,使用 likely() ,执行 if 后面的语句 的机会更大,使用unlikely(),执行else 后面的语句的机会更大。
例如下面这段代码,作者就认为 prev 不等于 next 的可能性更大,

if  (likely(prev  !=  next)) {
       next
-> timestamp  =  now;
        ...
else  {
        ...;
 }

通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着起面的代码,从而减少指令跳转带来的性能上的下降。


下面以两个例子来加深这种理解:

第一个例子: example1.c

int  testfun( int  x)
{
        
if (__builtin_expect(x,  0 )) {
                              
^^^---  We instruct the compiler,  " else "  block  is  more probable
                x 
=   5 ;
                x 
=  x  *  x;
        } 
else  {
                x 
=   6 ;
        }
        
return  x;
}

在这个例子中,我们认为 x 为0的可能性更大

编译以后,通过 objdump 来观察汇编指令,在我的 2.4 内核机器上,结果如下:

# gcc -O2 -c  example1.c
# objdump -d  example1.o

Disassembly of section .text:

00000000   < testfun > :
   
0 :    55                       push    % ebp
   
1 :    89  e5                   mov     % esp, % ebp
   
3 :   8b  45   08                 mov     0x8 ( % ebp), % eax
   
6 :    85  c0                   test    % eax, % eax
   
8 :    75   07                    jne     11   < testfun + 0x11 >
   a:   b8 
06   00   00   00           mov    $ 0x6 , % eax
   f:   c9                      leave
  
10 :   c3                      ret
  
11 :   b8  19   00   00   00           mov    $ 0x19 , % eax
  
16 :   eb f7                   jmp    f  < testfun + 0xf >


可以看到,编译器使用的是 jne (不相等跳转)指令,并且 else block 中的代码紧跟在后面。

  8 :    75   07                    jne     11   < testfun + 0x11 >
 a:   b8 
06   00   00   00           mov    $ 0x6 , % eax


第二个例子: example2.c


int  testfun( int  x)
{
        
if (__builtin_expect(x,  1 )) {
                              
^^^   ---  We instruct the compiler,  " if "  block  is  more probable
                x 
=   5 ;
                x 
=  x  *  x;
        } 
else  {
                x 
=   6 ;
        }
        
return  x;
}

在这个例子中,我们认为 x 不为 0 的可能性更大

编译以后,通过 objdump 来观察汇编指令,在我的 2.4 内核机器上,结果如下:

# gcc -O2 -c  example2.c
# objdump -d  example2.o


Disassembly of section .text:

00000000   < testfun > :
   
0 :    55                       push    % ebp
   
1 :    89  e5                   mov     % esp, % ebp
   
3 :   8b  45   08                 mov     0x8 ( % ebp), % eax
   
6 :    85  c0                   test    % eax, % eax
   
8 :    74   07                    je      11   < testfun + 0x11 >
   a:   b8 
19   00   00   00           mov    $ 0x19 , % eax
   f:   c9                      leave
  
10 :   c3                      ret
  
11 :   b8  06   00   00   00           mov    $ 0x6 , % eax
  
16 :   eb f7                   jmp    f  < testfun + 0xf >


这次编译器使用的是 je (相等跳转)指令,并且 if block 中的代码紧跟在后面。

   8 :    74   07                    je      11   < testfun + 0x11 >
   a:   b8 
19   00   00   00           mov    $ 0x19 , % eax
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值