• likely && unlikely in GCC


    在linux内核源码或一些比较成熟的c语言架构源码中,我们常会见到类似下面的代码:

    1 if (unlikely(!packet)) {
    2     return res_failed;   
    3 }
    4 
    5 // OR
    6 
    7 if (likely(packet->type = HTTP)) {
    8     do_something();
    9 }

    有的地方可能会用大写,LIKELY() / UNLIKELY(),意思一样。

    然后我们看一下unlikely和likely的定义,大部分都是类似如下的宏定义:

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

    GCC 中用的是 _G_BOOLEAN_EXPR(expr) 来代替 !!(expr), 意思一样,都是把expr或x转成相应布尔变量。

    两个定义无一例外调用了一个内置函数 __builtin_expect(bool expr,  int x)。

    先解释一下:  LIKELY 和 UNLIKELY 不会对原expr的布尔值产生任何影响,也就是说只要expr == true, LIKELY(expr) 与 UNLIKELY(expr) 都为 true。他们起的只是编译器优化作用。

    我们先测试一段代码:

     1 /**
     2  * @author Lhfcws
     3  * @file test__builtin_expect.c 
     4  * @time 2013-07-22
     5  **/
     6 
     7 #define LIKELY(x) __builtin_expect(!!(x), 1)
     8 #define UNLIKELY(x) __builtin_expect(!!(x), 0)
     9 
    10 int test_likely(int x) {
    11     if(LIKELY(x))
    12         x = 0x00;
    13     else
    14         x = 0xff;
    15 
    16     return x;
    17 }
    18 
    19 int test_unlikely(int x) {
    20     if(UNLIKELY(x))
    21         x = 0x00;
    22     else
    23         x = 0xff;
    24 
    25     return x;
    26 }
    27 
    28 int test_justif(int x) {
    29     if(x)
    30         x = 0x00;
    31     else
    32         x = 0xff;
    33 
    34     return x;
    35 }

    可见,三个函数唯一的区别就是 if (x) 那里。

    我们执行一下命令编译和反汇编(要用 __builtin_expect 的话  -fprofile-arcs 必须加):

    gcc -fprofile-arcs -c test__builtin_expect.c -o test__builtin_expect.o
    objdump -d test__builtin_expect > builtin.asm

    此时打开生成的asm文件,就可以看到上面代码由gcc编译生成的汇编代码。我们截取那三个函数的汇编源码查看。

     1 00000000 <test_likely>:
     2    0:    55                       push   %ebp
     3    1:    89 e5                    mov    %esp,%ebp
     4    3:    83 7d 08 00              cmpl   $0x0,0x8(%ebp)
     5    7:    0f 95 c0                 setne  %al
     6    a:    0f b6 c0                 movzbl %al,%eax
     7    d:    85 c0                    test   %eax,%eax
     8    f:    74 09                    je     1a <test_likely+0x1a>
     9   11:    c7 45 08 00 00 00 00     movl   $0x0,0x8(%ebp)
    10   18:    eb 23                    jmp    3d <test_likely+0x3d>
    11   1a:    c7 45 08 ff 00 00 00     movl   $0xff,0x8(%ebp)
    12   21:    a1 20 00 00 00           mov    0x20,%eax
    13   26:    8b 15 24 00 00 00        mov    0x24,%edx
    14   2c:    83 c0 01                 add    $0x1,%eax
    15   2f:    83 d2 00                 adc    $0x0,%edx
    16   32:    a3 20 00 00 00           mov    %eax,0x20
    17   37:    89 15 24 00 00 00        mov    %edx,0x24
    18   3d:    8b 4d 08                 mov    0x8(%ebp),%ecx
    19   40:    a1 28 00 00 00           mov    0x28,%eax
    20   45:    8b 15 2c 00 00 00        mov    0x2c,%edx
    21   4b:    83 c0 01                 add    $0x1,%eax
    22   4e:    83 d2 00                 adc    $0x0,%edx
    23   51:    a3 28 00 00 00           mov    %eax,0x28
    24   56:    89 15 2c 00 00 00        mov    %edx,0x2c
    25   5c:    89 c8                    mov    %ecx,%eax
    26   5e:    5d                       pop    %ebp
    27   5f:    c3                       ret    
    28 
    29 00000060 <test_unlikely>:
    30   60:    55                       push   %ebp
    31   61:    89 e5                    mov    %esp,%ebp
    32   63:    83 7d 08 00              cmpl   $0x0,0x8(%ebp)
    33   67:    0f 95 c0                 setne  %al
    34   6a:    0f b6 c0                 movzbl %al,%eax
    35   6d:    85 c0                    test   %eax,%eax
    36   6f:    74 09                    je     7a <test_unlikely+0x1a>
    37   71:    c7 45 08 00 00 00 00     movl   $0x0,0x8(%ebp)
    38   78:    eb 23                    jmp    9d <test_unlikely+0x3d>
    39   7a:    c7 45 08 ff 00 00 00     movl   $0xff,0x8(%ebp)
    40   81:    a1 10 00 00 00           mov    0x10,%eax
    41   86:    8b 15 14 00 00 00        mov    0x14,%edx
    42   8c:    83 c0 01                 add    $0x1,%eax
    43   8f:    83 d2 00                 adc    $0x0,%edx
    44   92:    a3 10 00 00 00           mov    %eax,0x10
    45   97:    89 15 14 00 00 00        mov    %edx,0x14
    46   9d:    8b 4d 08                 mov    0x8(%ebp),%ecx
    47   a0:    a1 18 00 00 00           mov    0x18,%eax
    48   a5:    8b 15 1c 00 00 00        mov    0x1c,%edx
    49   ab:    83 c0 01                 add    $0x1,%eax
    50   ae:    83 d2 00                 adc    $0x0,%edx
    51   b1:    a3 18 00 00 00           mov    %eax,0x18
    52   b6:    89 15 1c 00 00 00        mov    %edx,0x1c
    53   bc:    89 c8                    mov    %ecx,%eax
    54   be:    5d                       pop    %ebp
    55   bf:    c3                       ret    
    56 
    57 000000c0 <test_justif>:
    58   c0:    55                       push   %ebp
    59   c1:    89 e5                    mov    %esp,%ebp
    60   c3:    83 7d 08 00              cmpl   $0x0,0x8(%ebp)
    61   c7:    74 09                    je     d2 <test_justif+0x12>
    62   c9:    c7 45 08 00 00 00 00     movl   $0x0,0x8(%ebp)
    63   d0:    eb 23                    jmp    f5 <test_justif+0x35>
    64   d2:    c7 45 08 ff 00 00 00     movl   $0xff,0x8(%ebp)
    65   d9:    a1 00 00 00 00           mov    0x0,%eax
    66   de:    8b 15 04 00 00 00        mov    0x4,%edx
    67   e4:    83 c0 01                 add    $0x1,%eax
    68   e7:    83 d2 00                 adc    $0x0,%edx
    69   ea:    a3 00 00 00 00           mov    %eax,0x0
    70   ef:    89 15 04 00 00 00        mov    %edx,0x4
    71   f5:    8b 4d 08                 mov    0x8(%ebp),%ecx
    72   f8:    a1 08 00 00 00           mov    0x8,%eax
    73   fd:    8b 15 0c 00 00 00        mov    0xc,%edx
    74  103:    83 c0 01                 add    $0x1,%eax
    75  106:    83 d2 00                 adc    $0x0,%edx
    76  109:    a3 08 00 00 00           mov    %eax,0x8
    77  10e:    89 15 0c 00 00 00        mov    %edx,0xc
    78  114:    89 c8                    mov    %ecx,%eax
    79  116:    5d                       pop    %ebp
    80  117:    c3                       ret    

    如上,我们看到,貌似test_likely 和 test_unlikely 没什么区别, test_justif就是少了setne al开始的三行代码而已(实际上是执行__builtin_expect(!!(x), 1)的代码)。

    其实这证明了一件事: LIKELY 和 UNLIKELY 的调用不会影响最终结果,实际两者的结果是一样的。

    我们之前提到他们起的作用是优化,因此我们编译的时候加上优化指令。

    gcc -O2 -fprofile-arcs -c test__builtin_expect.c -o test__builtin_expect.o
    objdump -d test__builtin_expect > builtin_O2.asm

    得到汇编:

     1 00000000 <test_likely>:
     2    0:    83 ec 04                 sub    $0x4,%esp
     3    3:    8b 44 24 08              mov    0x8(%esp),%eax
     4    7:    83 05 00 00 00 00 01     addl   $0x1,0x0
     5    e:    83 15 04 00 00 00 00     adcl   $0x0,0x4
     6   15:    85 c0                    test   %eax,%eax
     7   17:    74 06                    je     1f <test_likely+0x1f>
     8   19:    31 c0                    xor    %eax,%eax
     9   1b:    83 c4 04                 add    $0x4,%esp
    10   1e:    c3                       ret    
    11   1f:    83 05 08 00 00 00 01     addl   $0x1,0x8
    12   26:    b8 ff 00 00 00           mov    $0xff,%eax
    13   2b:    83 15 0c 00 00 00 00     adcl   $0x0,0xc
    14   32:    eb e7                    jmp    1b <test_likely+0x1b>
    15   34:    8d b6 00 00 00 00        lea    0x0(%esi),%esi
    16   3a:    8d bf 00 00 00 00        lea    0x0(%edi),%edi
    17 
    18 00000040 <test_unlikely>:
    19   40:    83 ec 04                 sub    $0x4,%esp
    20   43:    8b 54 24 08              mov    0x8(%esp),%edx
    21   47:    83 05 10 00 00 00 01     addl   $0x1,0x10
    22   4e:    83 15 14 00 00 00 00     adcl   $0x0,0x14
    23   55:    85 d2                    test   %edx,%edx
    24   57:    75 17                    jne    70 <test_unlikely+0x30>
    25   59:    83 05 18 00 00 00 01     addl   $0x1,0x18
    26   60:    b8 ff 00 00 00           mov    $0xff,%eax
    27   65:    83 15 1c 00 00 00 00     adcl   $0x0,0x1c
    28   6c:    83 c4 04                 add    $0x4,%esp
    29   6f:    c3                       ret    
    30   70:    31 c0                    xor    %eax,%eax
    31   72:    eb f8                    jmp    6c <test_unlikely+0x2c>
    32   74:    8d b6 00 00 00 00        lea    0x0(%esi),%esi
    33   7a:    8d bf 00 00 00 00        lea    0x0(%edi),%edi
    34 
    35 00000080 <test_justif>:
    36   80:    83 ec 04                 sub    $0x4,%esp
    37   83:    8b 4c 24 08              mov    0x8(%esp),%ecx
    38   87:    83 05 20 00 00 00 01     addl   $0x1,0x20
    39   8e:    83 15 24 00 00 00 00     adcl   $0x0,0x24
    40   95:    31 c0                    xor    %eax,%eax
    41   97:    85 c9                    test   %ecx,%ecx
    42   99:    75 10                    jne    ab <test_justif+0x2b>
    43   9b:    83 05 28 00 00 00 01     addl   $0x1,0x28
    44   a2:    b0 ff                    mov    $0xff,%al
    45   a4:    83 15 2c 00 00 00 00     adcl   $0x0,0x2c
    46   ab:    83 c4 04                 add    $0x4,%esp
    47   ae:    c3                       ret    

    现在三个函数就有很明显的不同了。

    留意一下每个函数其中的三行代码:

    1 je ... / jne ...    ; 跳转
    2 xor %eap, %eap      ; 其实是 mov 0x00, %eap,改用 xor 是编译器自己的优化。结果等价。
    3 mov 0xff, %eap      ; x = 0xff

    可以看到,likely版本和unlikely版本最大的区别是跳转的不同。

    likely版本编译器会认为执行 x == true 的可能性比较大,因此将 x == false 的情况作为分支,减少跳转开销。

    同理,unlikely版本编译器会认为执行 x == false 的可能性比较大,因此将 x == true 的情况作为分支,减少跳转开销。

    总结:

    likely 和 unlikely 的使用实际上是为了分支优化,不影响结果,据传,众多程序员平常很少注意分支优化情况,因此gcc有了这个选项。。。

    unlikely 一般适用于(但不仅限于)一些错误检查,比如本文开头示例。likely适用于主分支场景,即根据期望大部分情况都会执行的场景。

  • 相关阅读:
    信号、事件与状态
    信号处理机制的范式分析
    三寒两倒七分饱
    血热的人吃什么好
    消息、信息与信号的区别
    Busy waiting
    事件的处理机制:单播、广播、链式路由、职责链。
    事件处理:pull与push
    响应式编程
    类、组件、人机交互
  • 原文地址:https://www.cnblogs.com/lhfcws/p/3205366.html
Copyright © 2020-2023  润新知