今天看到的一个有关cpu的问题,所以分享总结一下:

抛出一个问题

首先有一段代码

#include <algorithm>
#include <ctime>
#include <iostream>int main()
{// Generate dataconst unsigned arraySize = 32768;int data[arraySize];for (unsigned c = 0; c < arraySize; ++c)data[c] = std::rand() % 256;// !!! With this, the next loop runs faster.std::sort(data, data + arraySize);// Testclock_t start = clock();long long sum = 0;for (unsigned i = 0; i < 100000; ++i){// Primary loopfor (unsigned c = 0; c < arraySize; ++c){if (data[c] >= 128)sum += data[c];}}double elapsedTime = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;std::cout << elapsedTime << std::endl;std::cout << "sum = " << sum << std::endl;
}

这段代码是将数组排序后,再遍历数组,大于128的进行累加,并记录所消耗的时间。
当我调用sort的时候输出的消耗时间为:

然而当我将sort注释掉以后再运行的消耗时间出乎预料:

不使用sort反而耗时多了3倍,这个从算法的角度是绝对解释不通的。

后来我了解到了cpu当中的分支预测,不得不感叹cpu设计的精妙。我没有学过计算机体系结构,所以对cpu的架构不太了解,只是说对cpu流水线这些有依稀的印象,所以下面的解释会非常的浅显,希望将来有机会可以学习一下计算机体系结构。

分支预测的概念

以下是概念:

分支预测器(英语:Branch predictor)是一种数字电路,在分支指令执行结束之前猜测哪一路分支将会被运行,以提高处理器的指令流水线的性能。使用分支预测器的目的,在于改善指令管线化的流程。现代使用指令管线化处理器的性能能够提高,分支预测器对于现今的指令流水线微处理器获得高性能是非常关键的技术。

然后是我的大白话:

首先分析分支预测这个概念产生的原因。首先我们知道,cpu的运行是以流水线的方式进行的,程序运行的时候会有一条非常长的流水线。当遇到一个逻辑分支时,需要依赖给出的逻辑来判断接下来的执行时。如果进行等待,则会消耗非常长的时间,会产生所谓的流水线停顿(stalled)或流水线冒泡(bubbling)或流水线打嗝(hiccup),因此分支预测就出现了。


图中一个气泡在编号为3的始终频率中产生,指令运行被延迟。

分支预测器

分支预测器是一种数字电路,在分支指令执行前,猜测哪一个分支会被执行,能显著提高pipelines的性能。

条件分支通常有两路后续执行分支,not token时,跳过接下来的JMP指令,继续执行, token时,执行JMP指令,跳转到另一块程序内存去执行。

加入分支预测器后,为避免pipeline停顿(stream stalled),其会猜测两路分支哪一路最有可能执行,然后投机执行,如果猜错,则流水线中投机执行中间结果全部抛弃,重新获取正确分支路线上的指令执行。可见,错误的预测会导致程序执行的延迟。

回到最初那个问题

会产生分支预测的代码为:

            if (data[c] >= 128)sum += data[c];

当我们对数组进行排序后,前面的元素进入if语句的时候都为false,而后面的元素进入if语句的时候都为true,这种分支的都是朝一个方向运行的方式对分支预测来说是十分友好的,因为分支预测会根据历史记录来判断我应该执行哪个分支,而之前的分支走向都是同一方向,这样使后续的分支预测都会正确。

T = 分支命中
N = 分支没有命中data[] = 0, 1, 2, 3, 4, ... 126, 127, 128, 129, 130, ... 250, 251, 252, ...
branch = N  N  N  N  N  ...   N    N    T    T    T  ...   T    T    T  ...= NNNNNNNNNNNN ... NNNNNNNTTTTTTTTT ... TTTTTTTTTT  (非常容易预测)

而当我们使用无须数组时,if语句的走向变得完全随机,分支预测的结果将变得无法预测,错误率达到了50%。

data[] = 226, 185, 125, 158, 198, 144, 217, 79, 202, 118,  14, 150, 177, 182, 133, ...
branch =   T,   T,   N,   T,   T,   T,   T,  N,   T,   N,   N,   T,   T,   T,   N  ...= TTNTTTTNTNNTTTN ...   (完全随机--无法预测)

每一次分支预测的失败都导致了预测后的执行的中间结果全部抛弃,从分支处重新来过,导致的超时。

解决方法

利用位运算来代替if语句

位运算的知识:

|x| >> 31 = 0 # 非负数右移31为一定为0
~(|x| >> 31) = -1 # 0取反为-1-|x| >> 31 = -1 # 负数右移31为一定为0xffff = -1
~(-|x| >> 31) = 0 # -1取反为0-1 = 0xffff
-1 & x = x # 以-1为mask和任何数求与,值不变

所以可以通过以下代码替代上面的if语句

int t = (data[c] - 128) >> 31; # statement 1
sum += ~t & data[c]; # statement 2

或者不用移位运算

int t=-((data[c]>=128)); # generate the mask
sum += ~t & data[c]; # bitwise AND

结论

  1. 排序会影响分支预测。
  2. 可以使用位运算来替代if语句。

自己对于计算机底层的原理还需要加强

CPU当中的分支预测相关推荐

  1. if-else走天下,让CPU分支预测技术浮出水面

    关键字 圈复杂度 CPU分支预测机制 指令 吞吐量 IPS-每秒指令 GIPS-每秒十亿指令 延迟-皮秒 分支预测 if-else走天下 圈复杂度 void sort(int *A) { int i ...

  2. 阿里程序员工作小技巧:理解CPU分支预测,提高代码效率

    技术传播的价值,不仅仅体现在通过商业化产品和开源项目来缩短我们构建应用的路径,加速业务的上线速率,体现也会在优秀程序员在工作效率提升,产品性能优化和用户体验改善等小技巧方面的分享,以提高我们的工作能力 ...

  3. 一步步编写操作系统 31 cpu的分支预测 下

    让我们说说预测的算法吧. 对于无条件跳转,没啥可犹豫的,直接跳过去就是了.所谓的预测是针对有条件跳转来说的,因为不知道条件成不成立.最简单的统计是根据上一次跳转的结果来预测本次,如果上一次跳转啦,这一 ...

  4. 一步步编写操作系统 30 cpu的分支预测简介

    人在道路的分岔口时要预测哪条路能够到达目的地,面对众多选择时,计算机也一样要抉择,毕竟计算机的运行方式是以人的思路来设计的,计算机中的抉择其实就是人在抉择. cpu中的指令是在流水线上执行.分支预测, ...

  5. 阿里程序员工作小技巧 | 理解CPU分支预测,提高代码效率

    技术传播的价值,不仅仅体现在通过商业化产品和开源项目来缩短我们构建应用的路径,加速业务的上线速率,也会体现在优秀程序员在工作效率提升.产品性能优化和用户体验改善等小技巧方面的分享,以提高我们的工作能力 ...

  6. 用三元操作符替代if-else以降低CPU分支预测惩罚实现Unity内函数13倍提速

    测试对象 1,C# (Unity脚本) 2,C# DLL(mcs build的动态链接库再导入Unity) 3,C Native Code(LLVM编译后导入Unity) 被测试函数源码 两个随机数数 ...

  7. cpu 分支预测对性能的影响

    cpu 分支预测对性能的影响 现在的 cpu 一般都支持分支预测功能.维基百科中有以下描述: 在计算机体系结构中,分支预测器(英语:Branch predictor)是一种数字电路,在分支指令执行结束 ...

  8. CPU的流水线,分支预测与乱序执行

    流水线 转自:http://www.elecfans.com/emb/dsp/20180405657563.html 流水线的概念来源于工业制造领域,以汽车装配为例来解释流水线的工作方式,假设装配一辆 ...

  9. #C++# #likely# #unlikely#减少CPU流水线分支预测错误带来的性能损失

    目录 流水线技术 分支预测 什么是likely和unlikely likely/unlikely的原理 likely/unlikely的适用条件 C++20中的likely/unlikely 流水线技 ...

最新文章

  1. [转载] static class 静态类(Java)
  2. 假如易立竞吐槽程序员......
  3. 打开IT运维外包的“黑盒”
  4. mingw编译boost_1_66_0
  5. Day14:使用斯坦福 NER 软件包实现你自己的命名实体识别器
  6. 云计算背后的秘密(3)-BigTable
  7. 三招做出页面中的节奏与韵律
  8. 认证授权方案之授权揭秘 (上篇)
  9. 数据结构与算法--分治算法-最大子序列和问题
  10. 20140617 数组和链表的区别
  11. P3089 [USACO13NOV]POGO的牛Pogo-Cow
  12. 怎么用dos系统进入服务器,如何进入dos系统(非常实用的几个DOS使用技巧)
  13. 工具----9、浏览器攻击框架--(BeEF)
  14. Cylons工业机器人_机器人的是什么意思
  15. [GKCTF 2021]excel 骚操作
  16. Python图像增强
  17. Python实现简单的web爬虫信息处理系统
  18. LeetCode#546. 移除盒子 (Python解法+详细分析)
  19. jsp 按照学号查找学生_怎样做才可以用JSP实现只输入姓名或学号就可以进行查询...
  20. windows 无法删除文件,没有操作权限,右键文件属性查看安全-不可用

热门文章

  1. 层(Overlays)
  2. 数据,数据元素,数据项,数据对象的区别
  3. 软考高级证书可以积分50分
  4. 数据结构学习,哈希表(链地址)
  5. 个人很喜欢的番茄助手格式
  6. 电脑耳机有杂音怎么办?【详解】
  7. grub linux修复 pe,恢复Ubuntu GRUB引导的方法
  8. 基于大中台架构的电商业务中台最佳实践之一:业务中台总体架构介绍
  9. mysql语句distinct_MySQL DISTINCT语句
  10. ftp服务器上传不了文件怎么办,ftp服务器怎么上传不了文件