You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
13 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 03 | 通过你的CPU主频我们来谈谈“性能”究竟是什么
“性能”这个词,不管是在日常生活还是写程序的时候,都经常被提到。比方说,买新电脑的时候,我们会说“原来的电脑性能跟不上了”;写程序的时候,我们会说,“这个程序性能需要优化一下”。那么,你有没有想过,我们常常挂在嘴边的“性能”到底指的是什么呢?我们能不能给性能下一个明确的定义,然后来进行准确的比较呢?
在计算机组成原理乃至体系结构中,“性能”都是最重要的一个主题。我在前面说过,学习和研究计算机组成原理,就是在理解计算机是怎么运作的,以及为什么要这么运作。“为什么”所要解决的事情,很多时候就是提升“性能”。
## 什么是性能?时间的倒数
计算机的性能,其实和我们干体力劳动很像,好比是我们要搬东西。对于计算机的性能,我们需要有个标准来衡量。这个标准中主要有两个指标。
第一个是**响应时间**Response time或者叫执行时间Execution time。想要提升响应时间这个性能指标你可以理解为让计算机“跑得更快”。
![](https://static001.geekbang.org/resource/image/4c/96/4c87a1851aeb6857a323064859da6396.png?wh=1142*537 "图中是我们实际系统里性能监测工具NewRelic中的响应时间代表了每个外部的Web请求的执行时间")
第二个是**吞吐率**Throughput或者带宽Bandwidth想要提升这个指标你可以理解为让计算机“搬得更多”。
![](https://static001.geekbang.org/resource/image/27/27/27cab77c0eec95ec29792e6c3d093d27.png?wh=1142*300)
服务器使用的网络带宽,通常就是一个吞吐率性能指标
所以说,响应时间指的就是,我们执行一个程序,到底需要花多少时间。花的时间越少,自然性能就越好。
而吞吐率是指我们在一定的时间范围内,到底能处理多少事情。这里的“事情”,在计算机里就是处理的数据或者执行的程序指令。
和搬东西来做对比,如果我们的响应时间短,跑得快,我们可以来回多跑几趟多搬几趟。所以说,缩短程序的响应时间,一般来说都会提升吞吐率。
除了缩短响应时间我们还有别的方法吗当然有比如说我们还可以多找几个人一起来搬这就类似现代的服务器都是8核、16核的。人多力量大同时处理数据在单位时间内就可以处理更多数据吞吐率自然也就上去了。
提升吞吐率的办法有很多。大部分时候我们只要多加一些机器多堆一些硬件就好了。但是响应时间的提升却没有那么容易因为CPU的性能提升其实在10年前就处于“挤牙膏”的状态了所以我们得慎重地来分析对待。下面我们具体来看。
我们一般把性能,定义成响应时间的倒数,也就是:
性能 = 1/响应时间
这样一来响应时间越短性能的数值就越大。同样一个程序在Intel最新的CPU Coffee Lake上只需要30s就能运行完成而在5年前CPU Sandy Bridge上需要1min才能完成。那么我们自然可以算出来Coffee Lake的性能是1/30Sandy Bridge的性能是1/60两个的性能比为2。于是我们就可以说Coffee Lake的性能是Sandy Bridge的2倍。
过去几年流行的手机跑分软件就是把多个预设好的程序在手机上运行然后根据运行需要的时间算出一个分数来给出手机的性能评估。而在业界各大CPU和服务器厂商组织了一个叫作**SPEC**Standard Performance Evaluation Corporation的第三方机构专门用来指定各种“跑分”的规则。
![](https://static001.geekbang.org/resource/image/a5/22/a50a6cb9d3df027aeda5ee8e53b75422.png?wh=1142*929 "一份SPEC报告通常包含了大量不同测试的评分")
SPEC提供的CPU基准测试程序就好像CPU届的“高考”通过数十个不同的计算程序对于CPU的性能给出一个最终评分。这些程序丰富多彩有编译器、解释器、视频压缩、人工智能国际象棋等等涵盖了方方面面的应用场景。感兴趣的话你可以点击[这个链接](https://www.spec.org/cpu2017/results/cpu2017.html)看看。
## 计算机的计时单位CPU时钟
虽然时间是一个很自然的用来衡量性能的指标,但是用时间来衡量时,有两个问题。
**第一个就是时间不“准”**。如果用你自己随便写的一个程序来统计程序运行的时间每一次统计结果不会完全一样。有可能这一次花了45ms下一次变成了53ms。
为什么会不准呢这里面有好几个原因。首先我们统计时间是用类似于“掐秒表”一样记录程序运行结束的时间减去程序开始运行的时间。这个时间也叫Wall Clock Time或者Elapsed Time就是在运行程序期间挂在墙上的钟走掉的时间。
但是计算机可能同时运行着好多个程序CPU实际上不停地在各个程序之间进行切换。在这些走掉的时间里面很可能CPU切换去运行别的程序了。而且有些程序在运行的时候可能要从网络、硬盘去读取数据要等网络和硬盘把数据读出来给到内存和CPU。所以说**要想准确统计某个程序运行时间,进而去比较两个程序的实际性能,我们得把这些时间给刨除掉**。
那这件事怎么实现呢Linux下有一个叫time的命令可以帮我们统计出来同样的Wall Clock Time下程序实际在CPU上到底花了多少时间。
我们简单运行一下time命令。它会返回三个值第一个是**real time**也就是我们说的Wall Clock Time也就是运行程序整个过程中流逝掉的时间第二个是**user time**也就是CPU在运行你的程序在用户态运行指令的时间第三个是**sys time**是CPU在运行你的程序在操作系统内核里运行指令的时间。而**程序实际花费的CPU执行时间CPU Time就是user time加上sys time**。
```
$ time seq 1000000 | wc -l
1000000
real 0m0.101s
user 0m0.031s
sys 0m0.016s
```
在我给的这个例子里你可以看到实际上程序用了0.101s但是CPU time只有0.031+0.016 = 0.047s。运行程序的时间里,只有不到一半是实际花在这个程序上的。
备注你最好在云平台上找一台1 CPU的机器来跑这个命令在多CPU的机器上seq和wc两个命令可能分配到不同的CPU上我们拿到的user time和sys time是两个CPU上花费的时间之和可能会导致real time可能会小于user time+sys time。
![](https://static001.geekbang.org/resource/image/0b/00/0b340db019d7e389a2bde4c237ee4700.jpg?wh=2301*1083 "程序实际占用的CPU时间一般比Elapsed Time要少不少")
**其次即使我们已经拿到了CPU时间我们也不一定可以直接“比较”出两个程序的性能差异**。即使在同一台计算机上CPU可能满载运行也可能降频运行降频运行的时候自然花的时间会多一些。
除了CPU之外时间这个性能指标还会受到主板、内存这些其他相关硬件的影响。所以我们需要对“时间”这个我们可以感知的指标进行拆解把程序的CPU执行时间变成 CPU时钟周期数CPU Cycles和 时钟周期时间Clock Cycle的乘积。
程序的CPU执行时间=CPU时钟周期数×时钟周期时间
我们先来理解一下什么是时钟周期时间。你在买电脑的时候一定关注过CPU的主频。比如我手头的这台电脑就是Intel Core-i7-7700HQ 2.8GHz这里的2.8GHz就是电脑的主频Frequency/Clock Rate。这个2.8GHz我们可以先粗浅地认为CPU在1秒时间内可以执行的简单指令的数量是2.8G条。
如果想要更准确一点描述这个2.8GHz就代表我们CPU的一个“钟表”能够识别出来的最小的时间间隔。就像我们挂在墙上的挂钟都是“滴答滴答”一秒一秒地走所以通过墙上的挂钟能够识别出来的最小时间单位就是秒。
而在CPU内部和我们平时戴的电子石英表类似有一个叫晶体振荡器Oscillator Crystal的东西简称为晶振。我们把晶振当成CPU内部的电子表来使用。晶振带来的每一次“滴答”就是时钟周期时间。
在我这个2.8GHz的CPU上这个时钟周期时间就是1/2.8G。我们的CPU是按照这个“时钟”提示的时间来进行自己的操作。主频越高意味着这个表走得越快我们的CPU也就“被逼”着走得越快。
如果你自己组装过台式机的话可能听说过“超频”这个概念这说的其实就相当于把买回来的CPU内部的钟给调快了于是CPU的计算跟着这个时钟的节奏也就自然变快了。当然这个快不是没有代价的CPU跑得越快散热的压力也就越大。就和人一样超过生理极限CPU就会崩溃了。
我们现在回到上面程序CPU执行时间的公式。
程序的CPU执行时间=CPU时钟周期数×时钟周期时间
最简单的提升性能方案自然缩短时钟周期时间也就是提升主频。换句话说就是换一块好一点的CPU。不过这个是我们这些软件工程师控制不了的事情所以我们就把目光挪到了乘法的另一个因子——CPU时钟周期数上。如果能够减少程序需要的CPU时钟周期数量一样能够提升程序性能。
对于CPU时钟周期数我们可以再做一个分解把它变成“指令数×**每条指令的平均时钟周期数**Cycles Per Instruction简称CPI”。不同的指令需要的Cycles是不同的加法和乘法都对应着一条CPU指令但是乘法需要的Cycles就比加法要多自然也就慢。在这样拆分了之后我们的程序的CPU执行时间就可以变成这样三个部分的乘积。
程序的CPU执行时间=指令数×CPI×Clock Cycle Time
因此,如果我们想要解决性能问题,其实就是要优化这三者。
1. 时钟周期时间,就是计算机主频,这个取决于计算机硬件。我们所熟知的[摩尔定律](https://zh.wikipedia.org/wiki/%E6%91%A9%E5%B0%94%E5%AE%9A%E5%BE%8B)就一直在不停地提高我们计算机的主频。比如说我最早使用的80386主频只有33MHz现在手头的笔记本电脑就有2.8GHz在主频层面就提升了将近100倍。
2. 每条指令的平均时钟周期数CPI就是一条指令到底需要多少CPU Cycle。在后面讲解CPU结构的时候我们会看到现代的CPU通过流水线技术Pipeline让一条指令需要的CPU Cycle尽可能地少。因此对于CPI的优化也是计算机组成和体系结构中的重要一环。
3. 指令数,代表执行我们的程序到底需要多少条指令、用哪些指令。这个很多时候就把挑战交给了编译器。同样的代码,编译成计算机指令时候,就有各种不同的表示方式。
我们可以把自己想象成一个CPU坐在那里写程序。计算机主频就好像是你的打字速度打字越快你自然可以多写一点程序。CPI相当于你在写程序的时候熟悉各种快捷键越是打同样的内容需要敲击键盘的次数就越少。指令数相当于你的程序设计得够合理同样的程序要写的代码行数就少。如果三者皆能实现你自然可以很快地写出一个优秀的程序你的“性能”从外面来看就是好的。
## 总结延伸
好了学完这一讲对“性能”这个名词你应该有了更清晰的认识。我主要对于“响应时间”这个性能指标进行抽丝剥茧拆解成了计算机时钟周期、CPI以及指令数这三个独立的指标的乘积并且为你指明了优化计算机性能的三条康庄大道。也就是提升计算机主频优化CPU设计使得在单个时钟周期内能够执行更多指令以及通过编译器来减少需要的指令数。
在后面的几讲里面我会为你讲解具体怎么在电路硬件、CPU设计乃至指令设计层面提升计算机的性能。
## 课后思考
每次有新手机发布的时候,总会有一些对于手机的跑分结果的议论。乃至于有“作弊”跑分或者“针对跑分优化”的说法。我们能针对“跑分”作弊么?怎么做到呢?“作弊”出来的分数对于手机性能还有参考意义么?
欢迎留言和我分享你的思考和疑惑,你也可以把今天的内容分享给你的朋友,和他一起学习和进步。