Chinaunix首页 | 论坛 | 博客
  • 博客访问: 624110
  • 博文数量: 144
  • 博客积分: 5037
  • 博客等级: 大校
  • 技术积分: 1581
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-30 21:49
文章存档

2010年(16)

2009年(128)

分类: LINUX

2009-06-22 15:17:33

在linux中判断语句经常会看到likely和unlikely,例如:

if(likely(value)){
}
else{
}

简单从表面上看

if(likely(value)) == if(value)

if(unlikely(value)) == if(value)

    也就是likely和unlikely是一样的,但是实际上执行是不同的,加likely的意识是value的值为真的可能性更大一些,那么执行if的机会大,而unlikely表示value的值为假的可能性大一些,执行else机会大一些。加上这种修饰,编译成二进制代码时likely使得if后面的执行语句紧跟着前面的程序,unlikely使得else后面的语句紧跟着前面的程序,这样就会被cache预读取,增加程序的执行速度,likely和unlikely的实现在
include/linux/compiler.h中:
     9 #if __GNUC__ == 2 && __GNUC_MINOR__ < 96
     10 #define __builtin_expect(x, expected_value) (x)
     11 #endif
     12
     13 #define likely(x)   __builtin_expect((x),1)
     14 #define unlikely(x) __builtin_expect((x),0)

__builtin_expect是gcc的一个预处理命令,其解释如下:

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.

    The __builtin_expect is a method that gcc (versions >= 2.96) offer for programmers to indicate branch prediction information to the compiler. The return value of __builtin_expect is the first argument (which could only be an integer) passed to it.

To check it out how it could be beneficial, an excerpt from "info gcc" :

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

[This] would indicate that we do not expect to call `foo', since we expect `x' to be zero.

Based on this information the compiler generates intelligent code, such that the most expected result is favored.

Let us consider it with a simple example function :

[kedar@ashwamedha ~]$ cat abc.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;
}
 
[kedar@ashwamedha ~]$ gcc -O2 -c abc.c
[kedar@ashwamedha ~]$ objdump -d abc.o
 
abc.o: file format elf32-i386
 
Disassembly of section .text:
 
00000000 :
   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 >
                //^^^ --- The compiler branches the "if" block and keeps "else" sequential
   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 >

And let us see what happens if we make the "if" block more likely

[kedar@ashwamedha ~]$ cat abc.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;
}
                                                                                                    
[kedar@ashwamedha ~]$ gcc -O2 -c abc.c
[kedar@ashwamedha ~]$ objdump -d abc.o
                                                                                                    
abc.o: file format elf32-i386
                                                                                                    
Disassembly of section .text:
                                                                                                    
00000000 :
   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 >
                    //^^^ --- The compiler branches the "else" block and keeps "if" sequential
   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 >

friends:

unlikely is a hint about a boolean expression
February 15, 2007 - 3:47pm
farnz
In order:

If the else block is omitted, the compiler only executes the block of code in the if block if the if expression is true; thus, by omitting the else, you ensure that the compiler only has to consider branching on one result of the expression. The likely and unlikely macros tell the compiler which result is more likely from the given expression, so that it can make a better decision about which codepath branches, and which codepath doesn't.
unlikely(expression) is a hint to the compiler that expression will probably be false. You thus need unlikely() around each expression that'
s tested in an if, and likely to be false.
As an example, the following code can compile to two different forms:

/* before if */
if(error)
{
/* log error */
}
/* after if */

Either the compiler decides to branch if error is true, resulting in code like:

/* before if */
MOVS R0, error
BNE ifBlock
afterIf:
/* after if */
...
ifBlock:
/* log error */
B afterIf

Alternatively, it can decide to branch if error is false, resulting in code like

/* before if */
MOVS R0, error
BEQ afterIf
/* log error */
/* after if */

Taking a branch tends to be costly, so you want to avoid taking branches whenever you can. By using likely() and unlikely(), you can persuade the compiler to generate code that doesn't normally take the branch, which runs faster. In this example, putting unlikely() around error would make the compiler more likely to generate the first block of code (faster whenever error is false), whereas putting likely() around error would make the compiler more likely to generate the second block of code (faster whenever error is true).

Even if you have an else block, likely() and unlikely() can help; most CPUs have some form of static branch prediction, and likely() and unlikely() can be used by the compiler to help it generate code where the CPU'
s static branch predictor gets it right more often than not.

阅读(768) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~