C/C++ 分支预测(likely unlikely)

看一些代码时,会遇到likely unlikely, 查了查网上的资料,结合自己的理解记录一下。

1. 一些概念

  指令周期是指执行一条指令所需要的时间,一般由若干个机器周期组成,是从取指令、分析指令到指令执行完所需的全部。

  预取指令具体方法就是在不命中时,当数据从主存储器中取出送往CPU的同时,把主存储器相邻几个单元中的数据(称为一个数据块)都取出来送入Cache中。预取指令可以更好的利用 cpu资源。简单说就是从内存取指令很慢, cpu要等待这个过程。如果能提前预测可能执行的指令,就提前从内存把指令读到 cache, 由于 cache的访问速度较内存快,cpu要执行时就不用等很长时间了。

  如果开发人员可以告诉编译器,哪个分支更有可能发生(likely) 或者 非常不可能发生(unlikely), 可以帮助编译器进行代码编译

2. 看看代码

unlikely.cpp:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 
 4 #define likely(x) __builtin_expect(!!(x), 1) //gcc内置函数, 帮助编译器分支优化
 5 #define unlikely(x) __builtin_expect(!!(x), 0)
 6 
 7 int main(int argc, char* argv[]){
 8     int x = 0;
 9     x = atoi(argv[1]);
10 
11     if (unlikely(x == 3)){  //告诉编译器这个分支非常不可能为true
12         x = x + 9;
13     }
14     else{
15         x = x - 8;
16     }
17 
18     printf("x=%d
", x);
19     return 0;
20 }

3. 分析一下

gcc版本:gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

编译:gcc -O2 unlikely.cpp -o unlikely

反汇编一下,看看汇编:objdump -S unlikely

 1 08048380 <main>:
 2  8048380:   55                      push   %ebp
 3  8048381:   89 e5                   mov    %esp,%ebp
 4  8048383:   83 e4 f0                and    $0xfffffff0,%esp
 5  8048386:   83 ec 10                sub    $0x10,%esp
 6  8048389:   8b 45 0c                mov    0xc(%ebp),%eax
 7  804838c:   c7 44 24 08 0a 00 00    movl   $0xa,0x8(%esp)
 8  8048393:   00
 9  8048394:   c7 44 24 04 00 00 00    movl   $0x0,0x4(%esp)
10  804839b:   00
11  804839c:   8b 40 04                mov    0x4(%eax),%eax
12  804839f:   89 04 24                mov    %eax,(%esp)
13  80483a2:   e8 c9 ff ff ff          call   8048370 <strtol@plt>
14  80483a7:   83 f8 03                cmp    $0x3,%eax
15  80483aa:   74 1f                   je     80483cb <main+0x4b>
16  80483ac:   83 e8 08                sub    $0x8,%eax
17  80483af:   89 44 24 08             mov    %eax,0x8(%esp)
18  80483b3:   c7 44 24 04 60 85 04    movl   $0x8048560,0x4(%esp)
19  80483ba:   08
20  80483bb:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
21  80483c2:   e8 99 ff ff ff          call   8048360 <__printf_chk@plt>
22  80483c7:   31 c0                   xor    %eax,%eax
23  80483c9:   c9                      leave
24  80483ca:   c3                      ret
25  80483cb:   b8 0c 00 00 00          mov    $0xc,%eax
26  80483d0:   eb dd                   jmp    80483af <main+0x2f>
27  80483d2:   90                      nop
28  80483d3:   90                      nop

 我们从汇编代码可以看到,代码并不是按照顺序生成的。

unlikely分支(x==3)非常不可能发生,汇编代码生成到了最后。

这个对于庞大的代码还是非常有用的,毕竟在代码预期阶段,可以根据局部性原理把最可能发生的分支对应的指令缓存进来。

原文地址:https://www.cnblogs.com/xudong-bupt/p/7337240.html