将流水线引入cpu,可以提高cpu的效率。更简单的说,让cpu可以预先取出下一条指令,可以提供cpu的效率。如下图所示:

取指令 执行指令 输出结果
取指令 执行

可见,cpu流水钱可以减少cpu等待取指令的耗时,从而提高cpu的效率。
如果存在跳转指令,那么预先取出的指令就无用了。cpu在执行当前指令时,从内存中取出了当前指令的下一条指令。执行完当前指令后,cpu发现不是要执行下一条指令,而是执行offset偏移处的指令。cpu只能重新从内存中取出offset偏移处的指令。因此,跳转指令会降低流水线的效率,也就是降低cpu的效率。

综上,在写程序时应该尽量避免跳转语句。那么如何避免跳转语句呢?答案就是使用__builtin_expect。

这个指令是gcc引入的,作用是”允许程序员将最有可能执行的分支告诉编译器”。

这个指令的写法为:builtin_expect(EXP, N)。意思是:EXP==N的概率很大。一般的使用方法是将builtin_expect指令封装为LIKELY和UNLIKELY宏。这两个宏的写法如下。

1
2
#define LIKELY(x) __builtin_expect(!!(x), 1) 
#define UNLIKELY(x) __builtin_expect(!!(x), 0) //x很可能为假

在很多源码如Linux内核、Glib等,我们都能看到likely()和unlikely()这两个宏,通常这两个宏定义是下面这样的形式。
可以看出这2个宏都是使用函数 __builtin_expect()实现的, __builtin_expect()函数是GCC的一个内建函数(build-in function).

2. 函数声明

函数__builtin_expect()是GCC v2.96版本引入的, 其声明如下:

long __builtin_expect(long exp, long c);

2.1. 功能描述

由于大部分程序员在分支预测方面做得很糟糕,所以GCC 提供了这个内建函数来帮助程序员处理分支预测.

你期望 exp 表达式的值等于常量 c, 看 c 的值, 如果 c 的值为0(即期望的函数返回值), 那么 执行 if 分支的的可能性小, 否则执行 else 分支的可能性小(函数的返回值等于第一个参数 exp).

GCC在编译过程中,会将可能性更大的代码紧跟着前面的代码,从而减少指令跳转带来的性能上的下降, 达到优化程序的目的.

通常,你也许会更喜欢使用 gcc 的一个参数 ‘-fprofile-arcs’ 来收集程序运行的关于执行流程和分支走向的实际反馈信息,但是对于很多程序来说,数据是很难收集的。

2.2. 参数详解

  • exp
    exp 为一个整型表达式, 例如: (ptr != NULL)

  • c
    c 必须是一个编译期常量, 不能使用变量

2.3. 返回值

  返回值等于 第一个参数 exp

2.4. 使用方法

与关键字if一起使用.首先要明确一点就是 if (value) 等价于 if (__builtin_expert(value, x)), 与x的值无关.

例子如下:

例子1 : 期望 x == 0, 所以执行func()的可能性小

1
2
3大专栏  GCC的分支预测优化__builtin_expectbr/>4
5
6
7
8
if (__builtin_expect(x, 0))
{
func();
}
else
{
  //do someting
}

例子2 : 期望 ptr !=NULL这个条件成立(1), 所以执行func()的可能性小

1
2
3
4
5
6
7
8
if (__builtin_expect(ptr != NULL, 1))
{  
  //do something
}
else
{
  func();
}

例子3 : 引言中的likely()和unlikely()宏

首先,看第一个参数!!(x), 他的作用是把(x)转变成”布尔值”, 无论(x)的值是多少 !(x)得到的是true或false, !!(x)就得到了原值的”布尔值”

使用 likely() ,执行 if 后面的语句 的机会更大,使用 unlikely(),执行 else 后面的语句的机会更大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#define likely(x)    __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0) int (char *argv[], int argc)
{
int a; /* Get the value from somewhere GCC can't optimize */
a = atoi (argv[1]); if (unlikely (a == 2))
  {
a++;
}
else
  {
   a--;
  }
printf ("%dn", a); return 0;
}

3. RATIONALE(原理)

if else 句型编译后, 一个分支的汇编代码紧随前面的代码,而另一个分支的汇编代码需要使用JMP指令才能访问到.

很明显通过JMP访问需要更多的时间, 在复杂的程序中,有很多的if else句型,又或者是一个有if else句型的库函数,每秒钟被调用几万次,

通常程序员在分支预测方面做得很糟糕, 编译器又不能精准的预测每一个分支,这时JMP产生的时间浪费就会很大,

函数 __builtin_expert() 就是用来解决这个问题的.

GCC的分支预测优化__builtin_expect的更多相关文章

  1. __builtin_expect — 分支预测优化

    1.引言 在很多源码如Linux内核.Glib等,我们都能看到likely()和unlikely()这两个宏,通常这两个宏定义是下面这样的形式. #define likely(x) __builtin ...

  2. [开发笔记]GCC 分支预测优化

    #define likely(x) __builtin_expect(!!(x),1)#define unlikely(x) __builtin_expect(!!(x),0) 用于优化在做分支判断的 ...

  3. 现代中央处理器(CPU)是怎样进行分支预测的?

    人们一直追求CPU分支预测的准确率,论文Simultaneous Subordinate Microthreading (SSMT)中给了一组数据,如果分支预测的准确率是100%,大多数应用的IPC会 ...

  4. CPU 分支预测

    去年在安宁庄的时候, 有个同事阐述了一个观点:php中的if else  在执行时考虑到效率的原因,不会按我们的代码的顺序一条一条去试,而是随机找出一个分支,执行,如果不对,再随机找到一个分支 当时由 ...

  5. gcc options选项的优化及选择

    gcc options选项的优化 -c和-o都是gcc编译器的可选参数[options] -c表示只编译(compile)源文件但不链接,会把.c或.cc的c源程序编译成目标文件,一般是.o文件.[只 ...

  6. 分支预测(branch prediction)

    记录一个在StackOverflow上看到一个十分有趣的问题:问题. 高票答案的优化方法: 首先找到罪魁祸首: if (data[c] >= 128) sum += data[c]; 优化方案使 ...

  7. 从一段 Dubbo 源码到 CPU 分支预测的一次探险之旅

    每个时代,都不会亏待会学习的人. 大家好,我是 yes. 这次本来是打算写一篇 RocketMQ 相关文章的,但是被插队了,我也是没想到的. 说来也是巧最近在看 Dubbo 源码,然后发现了一处很奇怪 ...

  8. 如何在代码层面提供CPU分支预测效率

    关于分支预测的基本概念和详细算法可以参考我之前写的知乎回答,基本概念不再阐述了~~ https://www.zhihu.com/question/486239354/answer/2410692045 ...

  9. 【CPU微架构设计】利用Verilog设计基于饱和计数器和BTB的分支预测器

    在基于流水线(pipeline)的微处理器中,分支预测单元(Branch Predictor Unit)是一个重要的功能部件,它负责收集和分析分支/跳转指令的执行结果,当处理后续分支/跳转指令时,BP ...

随机推荐

  1. CGridCtrl设置行列不可拉伸

    m_HFlexGrid.SetColumnResize(FALSE); m_HFlexGrid.SetRowResize(FALSE);

  2. C++ 管道

    // PipeServer.cpp : Defines the entry point for the console application. // #include "stdafx.h& ...

  3. YAML的基本使用

    YAML 在Python中的应用 https://www.cnblogs.com/shaosks/p/7344771.html

  4. AD中内电层设置

    用于走线与普铜 内电层分割

  5. 自学Java第五章——《面向对象基础》

    5.1 类与对象 1.类:一类具有相同特性的事物的抽象描述. 对象:类的一个个体,实例,具体的存在. 类是对象的设计模板. 2.如何声明类? [修饰符] class 类名{    成员列表:属性.方法 ...

  6. HZNU-ACM寒假集训Day6小结 线性DP

    线性DP 考虑一组硬币面值 1,5,11 给定W,求凑出W的最少硬币个数 我们记凑出n需要用到的最少硬币数量为f(n)   我们注意到了一个很棒的性质 : f(n)只与f(n-1) f(n-5) f( ...

  7. h5-动画基本介绍

    1.介绍 *{ ; ; } div{ width: 200px; height: 200px; background-color: #5aff61; /*添加动画效果*/ /*1.animation- ...

  8. freeswitch初识

    一.参考内容 1.<FreeSWITCH权威指南>杜金房.张令考著 2.FreeSwitch 国内源码快速下载 3.Ubuntu下freeSwitch安裝指导(推荐) 4.Freeswit ...

  9. npm全局权限设置 - Mac

    网络上的常见方法 目前网络上常见的方法都是修改npm安装路径的权限 通过使用命令 npm config get prefix 得到npm的路径,结果大部分都是/usr/local.然后,很多方法都会要 ...

  10. Python Learning Day2

    练习:login功能 def login(): with open(r'C:\Users\liubin\desktop\user.txt','r') as f: res=f.read() flag=1 ...