Linux-Kernel-Learning icon indicating copy to clipboard operation
Linux-Kernel-Learning copied to clipboard

__builtin_expect详解

Open hangc0276 opened this issue 12 years ago • 0 comments

__builtin_expect是GCC(version>=2.9)引进的宏,其作用就是帮助编译器判断条件跳转的预期值,避免跳转造成时间乱费。拿段代码来说:

#define likely(x)   __builtin_expect(!!(x), 1)  表示大多数情况下likely(x)条件为真,其存在形式一般为if(likely(x))
#define unlikely(x) __builtin_expect(!!(x), 0)  表示大多数情况下unlikely(x)条件为假,其存在形式为if(unlikely(x))

具体内部汇编实现过程,参看下面程序:

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

int test_likely(int x)
{
        if(likely(x))
                x = 5 ;
        else x = 6 ;
        return x ;
}

int test_unlikely(int x)
{
        if(unlikely(x))
                x = 5 ;
        else x = 6 ;
        return x ;
}

编译: [hangc@localhost test]$ gcc -fprofile-arcs -O2 -c test_builtin_expect.c 在这里使用了-fprofile-arcs参数,当用gcc编译文件的时候,如果带有-ftest-coverage参数,就会生成这个.gcno文件,它包含了程序块和行号等信息。接下来使用objdump对生成的test_builtin_expect.o进行反汇编,通过汇编代码观察__builtin_expect对程序的影响。

[hangc@localhost test]$ objdump -d test_builtin_expect.o

test_builtin_expect.o:     file format elf32-i386


Disassembly of section .text:

00000000 <test_likely>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   8b 45 08                mov    0x8(%ebp),%eax
   6:   85 c0                   test   %eax,%eax    -->对eax寄存器中的值按位相与
   8:   74 15                   je     1f <test_likely+0x1f>  -->关键
   a:   83 05 00 00 00 00 01    addl   $0x1,0x0
  11:   b8 05 00 00 00          mov    $0x5,%eax
  16:   83 15 04 00 00 00 00    adcl   $0x0,0x4
  1d:   5d                      pop    %ebp
  1e:   c3                      ret    
  1f:   83 05 08 00 00 00 01    addl   $0x1,0x8
  26:   b8 06 00 00 00          mov    $0x6,%eax
  2b:   83 15 0c 00 00 00 00    adcl   $0x0,0xc
  32:   5d                      pop    %ebp
  33:   c3                      ret    
  34:   8d b6 00 00 00 00       lea    0x0(%esi),%esi
  3a:   8d bf 00 00 00 00       lea    0x0(%edi),%edi

00000040 <test_unlikely>:
  40:   55                      push   %ebp
  41:   89 e5                   mov    %esp,%ebp
  43:   8b 55 08                mov    0x8(%ebp),%edx
  46:   85 d2                   test   %edx,%edx
  48:   75 15                   jne    5f <test_unlikely+0x1f>  -->关键
  4a:   83 05 18 00 00 00 01    addl   $0x1,0x18
  51:   b8 06 00 00 00          mov    $0x6,%eax
  56:   83 15 1c 00 00 00 00    adcl   $0x0,0x1c
  5d:   5d                      pop    %ebp
  5e:   c3                      ret    
  5f:   83 05 10 00 00 00 01    addl   $0x1,0x10
  66:   b8 05 00 00 00          mov    $0x5,%eax
  6b:   83 15 14 00 00 00 00    adcl   $0x0,0x14
  72:   5d                      pop    %ebp
  73:   c3                      ret    
  74:   8d b6 00 00 00 00       lea    0x0(%esi),%esi
  7a:   8d bf 00 00 00 00       lea    0x0(%edi),%edi

00000080 <_GLOBAL__I_65535_0_test_likely>:
  80:   55                      push   %ebp
  81:   89 e5                   mov    %esp,%ebp
  83:   83 ec 18                sub    $0x18,%esp
  86:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  8d:   e8 fc ff ff ff          call   8e <_GLOBAL__I_65535_0_test_likely+0xe>
  92:   c9                      leave  
  93:   c3                      ret

两个函数编译生成的汇编语句所使用到的跳转指令不一样,仔细分析下会发现__builtin_expect实际上是为了满足在大多数情况不执行跳转指令,所以__builtin_expect仅仅是告诉编译器优化,并没有改变其对真值的判断。 这种用法在linux内核中也经常用到,国外也有一篇相关的文章,大家不妨看看:http://kernelnewbies.org/FAQ/LikelyUnlikely

hangc0276 avatar Dec 04 '13 08:12 hangc0276