来源 | 码农的荒岛求生

责编 | 寇雪芹

头图 | 下载于ICphoto

18世纪流水线的诞生带来了制造技术的变革,人类当今拥有琳琅满目物美价廉的商品和流水线技术的发明密不可分,因此当你喝着可乐、吹着空调、坐在特斯拉里拿着智能手机刷这篇文章时需要感谢流水线技术

一段有趣的代码

有这样一段代码:

for (int k = 0; k < 10000; k++){for (int i = 0; i < arr.size(); i++) {if (arr[i] > 256)sum += arr[i];}
}

这段代码非常简单,给定一个数组,计算所有大于256 的元素之和,重复计算 10000 遍。这段代码本身平淡无奇,但有趣的是:如果这个数组是有序的,那么这段代码的运行速度会比处理无序数组快将近 10 倍(不同的机器、CPU架构可能会稍有差异)。可这是为什么呢?这和制造业使用的流水线又有什么关系呢?且听我慢慢道来。

流水线技术的诞生

1769年,英国人乔赛亚·韦奇伍德开办了一家陶瓷工厂,这家工厂生产的陶瓷乏善可陈,但其内部的管理方式极具创新性,传统的方法都是由制陶工专人来完成,但韦奇伍德研究后将整个制陶工艺流程分成了几十道工序,每一道工序都交给专人完成,这样传统的制陶人不复存在,这便是工业流水线最早的雏形。

虽然流水线技术可以说是英国人发明的,但发扬光大的却是美国人,这便是福特与T型车。

20世纪初,福特将流水线技术应用到汽车的批量生产,效率得到千倍提高,使得汽车这种奢侈品开始能够为大众消费,深刻影响了现代社会的方方面面,注意上图中一辆车的价格。

100 年后又一个美国人携带他的时尚电动车再一次席卷全球,这就是特斯拉。

接下来我们仔细看一下流水线技术。

特斯拉与流水线

假设组装一辆特斯拉需要经过:组装车架、安装引擎、安装电池、检验四道工序,同时假设每个步骤需要 20 分钟,因此如果所有工序都由一个组装站点来完成,那么组装一辆特斯拉需要80分钟。

但如果每个步骤都交给一个特定站点来组装的话就不一样了,此时生产一辆车的时间依然是80分钟,但这只是第一辆车所需要的时间,此后工厂可以每20分钟就交付一辆特斯拉。

注意,流水线并没有减少组装一辆车的时间,只是增加了工厂的吞吐能力。流水线技术的使用极大增加了工厂交付车辆的效率。

CPU 与超级工厂

其实 CPU 本身也是一座超级工厂。只不过CPU这座工厂生产的不是特斯拉,而是机器指令。工厂内部有流水线极大提高了生产效率,CPU 没有理由不拥有。

你可以想象一下,不管你现在看这篇文章用的是PC 还是智能手机,其内部的 CPU 都有一条复杂度不亚于特斯拉超级工厂的流水生产线

如果我们把CPU处理的一条机器指令当做一辆特斯拉的话,那么对于现代CPU这座超级工厂来说,一秒钟的时间内可以交付数十亿量特斯拉,效率完爆任何当今制造界的工业流水线,CPU 才是一座名副其实的超级工厂

如果特斯拉超级工厂也如 CPU 一般高效的话,特斯拉可能比现在的自行车都要便宜,地球人民人手一辆特斯拉不成问题,算上外星人也不成问题。

机器指令与流水线

实际上说 CPU 生产机器指令是不正确的,CPU 其实不是在生产机器指令而是在处理机器指令,生产机器指令的是编译器,CPU需要处理机器指令以此来指挥整个计算机系统工作。

同生产一辆特斯拉需要四道工序一样,处理一条机器指令大体上也可以分为四个步骤:取指、译码、执行、回写,这几个阶段分别由特定的硬件来完成 (注意,真实 CPU 内部可能会将执行一条指令分解为数十个阶段)。

怎么样,是不是和超级工厂生产特斯拉没什么区别,当今CPU用每秒处理数十亿机器指令的能力驱动着智能手机好让你流畅的刷公众号、短视频、刷微博、刷知乎,这里,流水线技术功不可没。

当 if 遇到流水线

实际上 CPU 内部的流水线和现实中的并不完全一样。程序员在代码中编写的 if 语句一般会被编译器翻译成一条跳转指令,if 语句其实起到一种分支的作‍‍用,如果条件成立则需要执行if内部的逻辑,否则不执行;因此跳转指令会依赖自身的执行结果来决定到底要不要跳转,这会对流水线产生影响。

有的同学可能不明白,这能产生什么影响呢?

现在,让我们仔细观察一下特斯拉流水线,你会发现当前一辆车还没有完全制造完成时后一辆车就已经进入到流水线了

对于CPU来说道理是一样的,当一条跳转指令还没有完成时后面的指令就需要进入到流水线,因此问题来了:

跳转指令需要依赖自身的执行结果来决定到底要不要跳转,那么在跳转指令没有执行完的情况下 CPU 怎么知道后面哪个分支的指令能进入到流水线呢

CPU 能预测未来吗?

预测未来

对此 CPU 当然是不知道的。那么该怎么办呢?很简单,一个字,

你没有看错,CPU 会猜一下 if 语句可能会走哪个分支,如果猜对了流水线照常继续,如果猜错了,对不起,流水线上已经执行的后续指令全部作废,因此我们可以看到如果CPU猜错了会有性能损耗。

现代 CPU 将“猜”的这个过程称为分支预测

当然,CPU 中的分支预测并不是简单的抛硬币式的随机瞎猜,而且有特定策略,比如可能会基于执行跳转指令的历史去进行预测等等。

知道了分支预测就可以解释文章开头的问题了。

现在我们知道,程序员编写的 if 语句对应的是跳转指令:

if (arr[i] >= 256) {sum += arr[i];}

CPU 在执行完跳转指令之前必须决定后续哪个分支的指令会进入到流水线,猜对了流水线照常进行,猜错了有性能损耗。

那么如果一个数组是有序的:

而如果一个数组是无序的:

你觉得哪种更好猜一些?

如果你给CPU一个无序数组,那么 Arr[i] 是否大于256 基本上就是随机的,对于随机事件,不要说CPU的分支预测,任何其它预测手段都将无效,否则这就不是随机事件了

如果 CPU 猜的不对,那么流水线上的后续指令将作废,这就解释了为什么处理有序数组要比处理无序数组性能好了,因为在数组有序的情况下,CPU 的分支预测几乎不会猜错,流水线上的指令不会被频繁作废。

这对程序员的启示就是:如果你编写了 if 语句,那么你最好让 CPU 大概率能猜对

有的同学看到这里,可能会觉得每一条 if 语句都性能低下,恨不得从此不再写if else,真的是这样吗?

编写 If else时需要注意什么

实际上如果你编写的if语句没有位于对性能要求很高的核心代码部分,那么分支预测失败这种问题无需关心。

实际上现代 CPU 的分支预测是很聪明的,对于非核心部分的if 语句分支预测失败带来的性能损失可以忽略不计。

但是对于文章开头提到的代码,程序的大部分时间都用在了 for 循环中,这时你就要注意了,当然前提还是这段代码对时间要求非常严苛,否则你也没必要为了这点性能去优化。

好奇的同学可能会问,如果给定的数组是无序的,那么上面提到的这段该怎么优化呢?

性能优化

实际上非常简单,只需要移除 if 语句就可以,该怎么移除呢?

没有 if 语句的话,那么 sum 每次都必须加上一个数,如果arr[i]比256大,那么 sum 加上差值,否则 sum 加 0即可,这样就消除了if 判断。

我们计算arr[i] - 256的值,并将其向右移动31位:

(arr[i] - 256) >> 31

这样得到的数不是0 (0x00000000),就是 -1 (0xffffffff),然后我们对其取反,再次与上 arr[i] 即可:

sum += ~((arr[i] - 256) >> 31) & arr[i];

也就是说如果arr[i] - 256 大于0 的话那么差值会与上 0xffffffff,其结果就是保持不变,否则会与上0,其结果就是sum会加上0,这样就不需要 if 判断了。

利用位运算,即使数组是无序的也不会有性能问题,代价就是代码可读性会降低很多,这里,我们再一次看到天下没有免费的午餐

总结

虽然 CPU 体积很小,只有指甲那么大,但 CPU 可能是人类有史以来建造过的最复杂的东西,在这里实现了很多有趣的功能,程序员只有彻底理解 CPU 才能更好的利用这些功能编写性能优异的程序。

希望这篇对大家理解 CPU 有所帮助。


2020-2021中国开发者调查报告重磅来袭,直接扫码或微信搜索「CSDN」公众号,后台回复关键词「开发者」,快速获取完整的报告内容!


更多精彩推荐
☞如何部署一个Kubernetes集群☞继云计算巨头失火后,微软决定送数据中心去“泡澡”!☞走过 30 年:银行数据库的下一步是国产化
点分享点收藏点点赞点在看

发掘 CPU 与超级工厂的共性,程序员的心思你别猜相关推荐

  1. 特斯拉遇上 CPU:程序员的心思你别猜

    作者 | 码农的荒岛求生 来源 | 码农的荒岛求生 图源 | 视觉中国 18世纪流水线的诞生带来了制造技术的变革,人类当今拥有琳琅满目物美价廉的商品和流水线技术的发明密不可分,因此当你喝着可乐.吹着空 ...

  2. 骚操作!用 CPU 烤肉,这位程序员做到了

    整理 | 王晓曼 出品 | 程序人生(ID:coder_life) 高端的食材往往采用最朴素的烹饪方式. 这不,近日,西二旗某公司程序员加班到深夜用CPU烤肉的视频火了.视频中,程序员小哥熟练地打开电 ...

  3. c语言查看cpu温度代码_很多程序员都不知道,C语言中还有“快类型”和“小类型”...

    虽说C语言是一门很成熟的编程语言,但是近些年来也是有所发展的,从早期的C89到后来的C99.C11等新标准,C语言逐步增加了许多好用的功能,例如新标准头文件"stdint.h"的添 ...

  4. 骚操作!用 CPU 烤肉,这位程序员做到了!

    高端的食材往往采用最朴素的烹饪方式. 这不,近日,西二旗某公司程序员加班到深夜用CPU烤肉的视频火了.视频中,程序员小哥熟练地打开电脑机箱,一边在CPU上烤肉一边敲代码. 我国新疆的夏天,地表温度最强 ...

  5. 程序员懂不懂计算机硬件配置清单,你还不懂硬盘,内存和CPU的关系 ?(程序员入门)...

    你好我是辰兮,很高兴你能来阅读,本篇文章小结了硬盘,内存和CPU的关系,献给初学者,分享获取新知,大家共同进步. 文章目录 一.硬盘内存CPU图 二.硬盘讲解 三.内存的简介 四.拓展A盘和B盘 一. ...

  6. 你还不懂硬盘,内存和CPU的关系 ?(程序员入门)

    你好我是辰兮,很高兴你能来阅读,本篇文章小结了硬盘,内存和CPU的关系,献给初学者,分享获取新知,大家共同进步. 文章目录 一.硬盘内存CPU图 二.硬盘讲解 三.内存的简介 四.拓展A盘和B盘 一. ...

  7. 猿创征文|程序员的浪漫(代码猜诗词)

      ✅作者简介: 全栈领域新星创作者,阿里云专家博主,华为云云享专家博主,掘金后端评审团成员,                         

  8. 不懂精简指令集还敢说自己是程序员?

    ????????关注后回复 "进群" ,拉你进程序员交流群???????? 作者丨码农的荒岛求生 来源丨码农的荒岛求生 内存与编译器 时间来到了1980s年代,此时容量" ...

  9. Java 程序员薪资这么高,取决于什么?

    众多行业中,程序员当然属于高薪职业. 无论是国内还是国外,IT行业的程序员.工程师,甚至连码农都要比其他行业的从业者的收入高很多! 但是Java程序员拿多少钱跟有多少经验有关系,但经验的多少跟年限没有 ...

最新文章

  1. 网站托管运营需要注意哪些内容?
  2. xshell使用xftp传输文件 使用pure-ftpd搭建ftp服务
  3. 文献记录(part55)--基于分布式非负矩阵分解的大规模主题社区挖掘
  4. 排查一个触摸屏驱动问题
  5. linux输入法_超强两笔输入法 入门简文
  6. 简单防止通过执行存储过程攻击服务器
  7. java day65【视图[应用] 、索引[应用] 、pl/sql 基本语法[了解] 、存储过程[理解] 、 存储函数[理解]、 触发器[理解]、Java 程序调用存储过程[应用]】...
  8. JavaWeb:(练习)十四、基于mybatis框架的简单的学生管理系统
  9. android 设置全屏
  10. 学田岛电脑绣花制版新人上手教程之一
  11. 从一份外卖报告折射出的就业新选择
  12. 生物信息流程开发之甲基化分析pipeline
  13. 计算机网络-数据链路层 1
  14. 边缘计算赋能智慧城市:机遇与挑战
  15. 01.Windows系统安装
  16. 塔望食业洞察|预制菜行业市场现状、发展趋势及未来思考
  17. mysql 查看系统参数_查看MYSQL系统参数
  18. 批量下载:斯坦福大学公开课-编程范式,来源:网易公开课
  19. zxing 生成二维码 带logo
  20. 使用VS进行debug时的工作目录位置

热门文章

  1. php 模拟并发请求_PHP模拟并发请求
  2. 车站信号计算机联锁系统英语,车站信号计算机联锁-复习题
  3. 鸿蒙os2.0公测机型,鸿蒙OS2.0第二期第三期公测机型陆续公布 麒麟980和麒麟820将登场...
  4. 2020年度中国生命科学十大进展揭晓
  5. 一作发14篇SCI,累计IF60,博士前两年,他也曾走过弯路
  6. 线性代数拾遗(一):线性方程组、向量方程和矩阵方程
  7. 随机过程在数据科学和深度学习中有哪些应用?
  8. 【文末有福利】卷积学习与图像识别的技术发展
  9. 冷知识:数学常数“e”的传奇故事
  10. 人类最强运载火箭诞生!马斯克吹的牛再次兑现!