一.简介

“练拳不练功,到老一场空”。计算机底层原理,跟上层思想紧密相连。

二.计算机组成

2.1 计算机基本硬件组成

CPU

计算机最重要的核心配件,全名叫中央处理器。

计算机的所有“计算”都是由CPU来进行的。自然,CPU也是整台计算机造价最昂贵的部分之一。

内存

你撰写的程序、打开的浏览器、运行的游戏,都要加载到内存里才能运行。程序读取的数据、计算得到的结果,也都要放在内存里。内存越大,能加载的东西自然也就越多。

主板

存放在内存里的程序和数据,需要被CPU读取,CPU计算完之后,还要把数据写回内存,然而CPU不能直接插到内存上,反之亦然。

主板是一个有着各种各样,有时候多达数十乃至上百个插槽的配件,我们的CPU要插在主板上,内存也要插在主板上,主板的芯片组(Chipset)和总线(Bus)解决了CPU和内存之间如何通信问题,芯片组控制了数据传输的流转,也就是数据从哪里到哪里的问题。总线则是实际数据传输的高速公路。因此 ,总线速度(Bus Speed)决定了数据能传输的多快。

有了三大件,只要配上电源供电,计算机差不多可以跑起来了,但是现在还缺少各类输入(Input)/ 输出(Output)设备,也就是我们常说的 I/O 设备。如果你用的是自己的个人电脑,那显示器肯定必不可少,只有有了显示器我们才能看到计算机输出的各种图像、文字,这也就是所谓的输出设备。

显示器、鼠标、键盘和硬盘这些东西并不是一台计算机必须的部分。其实只需要有 I/O 设备,能让我们从计算机里输入和输出信息。

显卡

现在,使用图形界面操作系统的计算机,无论是 Windows、Mac OS 还是 Linux,显卡都是必不可少的。

现在的主板都带了内置的显卡,如果你用计算机玩游戏,做图形渲染或者跑深度学习应用,你多半就需要买一张单独的显卡,插在主板上。显卡之所以特殊,是因为显卡里有除了 CPU 之外的另一个“处理器”,也就是 GPU(Graphics Processing Unit,图形处理器),GPU 一样可以做各种“计算”的工作。

南桥

鼠标、键盘以及硬盘,这些都是插在主板上的,作为外部I/O设备,它们是通过主板上的南桥(SouthBridge)芯片组,来控制和CPU之间通信。“南桥”芯片的名字很直观,一方面,它在主板上的位置,通常在主板的“南面”。另一方面,它的作用就是作为“桥”,来连接鼠标、键盘以及硬盘这些外部设备和 CPU 之间的通信。

北桥

有了南桥,自然对应着也有“北桥”。以前主板上通常也有“北桥”芯片,用来作为“桥”,连接CPU和内存、显卡之间通信。不过随着时间的变迁,现在主板上的“北桥”芯片的工作,已经被移到CPU内部,所有你在主板上,已经看不到北桥芯片了。

2.2 冯·诺依曼体系结构

手机里只有SD卡这样类似硬盘功能存储卡插槽,并没有内存插槽,CPU插槽,因为手机尺寸原因,手机制造商门选择把CPU、内存、网络通信、乃至摄像头芯片,都封装到一个芯片,然后再嵌入到手机主板上。这种方式SoC,也就是System on a Chip(系统芯片)。

写智能手机上的 App,和写个人电脑的客户端应用似乎没有什么差别,都是通过“高级语言”这样的编程语言撰写、编译之后,一样是把代码和数据加载到内存里来执行。这是为什么呢?因为,无论是个人电脑、服务器、智能手机,还是 Raspberry Pi 这样的微型卡片机,都遵循着同一个“计算机”的抽象概念。

计算机祖师爷之一冯·诺依曼(John von Neumann)提出的冯·诺依曼体系结构(Von Neumann architecture),也叫存储程序计算机。

存储程序计算机(可编程,可存储)

2.2.1 可编程计算机

不可编程

计算机是由各种门电路组合而成的,然后通过组装出一个固定的电路板,来完成一个特定的计算程序。一旦需要修改功能,就要重新组装电路。

因为程序在计算机硬件层面是“写死”的。

2.2.2 存储计算机

程序本身是存储在计算机内存里,可以通过加载不同的程序来解决不同的问题。

有“存储程序计算机”,自然也有不能存储程序的计算机。典型的就是早年的“Plugboard”这样的插线板式的计算机。整个计算机就是一个巨大的插线板,通过在板子上不同的插头或者接口的位置插入线路,来实现不同的功能。这样的计算机自然是“可编程”的,但是编写好的程序不能存储下来供下一次加载使用,不得不每次要用到和当前不同的“程序”的时候,重新插板子,重新“编程”。

计算机基本组成_响应时间

著名的Engima Machine就用到了 Plugboard 来进行“编程”

可以看到,无论是“不可编程”还是“不可存储”,都会让使用计算机效率大大下降,而这个对效率的追求,也就是“存储程序计算机”由来。

2.2.3 第一份草案

冯祖师爷,基于当时在秘密开发的 EDVAC 写了一篇报告First Draft of a Report on the EDVAC

https://en.wikipedia.org/wiki/First_Draft_of_a_Report_on_the_EDVAC),描述了他心目中的一台计算机应该长什么样。这篇报告在历史上有个很特殊的简称,叫 First Draft,翻译成中文,其实就是《第一份草案》。这样,现代计算机的发展就从祖师爷写的一份草案开始了。

首先是一个包含算术逻辑单元(Arithmetic Logic Unit,ALU)和处理寄存器(Processor Register)的处理单元,用来完成各种算术和逻辑运算,。因为它能够完成各种数据的处理或者计算工作,因此也有人把这个叫作数据通路(Datapath)或者运算器。

然后一个包含指令寄存器(Instruction Register)和程序计数器(Program Counter)的控制单元(Control Unit/CU)),用来控制程序的流程,通常就是不同条件下的分支和跳转。在现在的计算机里,上面的算术逻辑单元和这里的控制器单元,共同组成了我们说的 CPU。

接着是用来存储数据(Data)和指令(Instruction)的内存,以及更大容量的外部存储,在过去,可能是磁带、磁鼓这样的设备,现在通常就是硬盘。

最后就是各种输入和输出设备,以及对应的输入和输出机制。我们现在无论是使用什么样的计算机,其实都是和输入输出设备在打交道。

任何一台计算机的任何一个部件都可以归到运算器、控制器、存储器、输入设备和输出设备中,而所有的现代计算机也是基于这个基础架构来设计开发的。

而所有的计算机程序,也都可以抽象为从输入设备读取输入信息,通过运算器和控制器来执行存储在存储器里的程序,最终把结果输出到输出设备中。而我们所有撰写的无论高级还是低级语言的程序,也都是基于这样一个抽象框架来进行运作的。

计算机基本组成_计算机组成_02

2.3 组成原理知识地图

计算机基本组成_1024程序员节_03

三.性能

3.1 什么是性能?时间的倒数

衡量计算性能两个标准:响应时间和吞吐率。

响应时间

执行时间,想要提升响应时间这个性能指标,你可以理解为让计算机“跑得更快”。

计算机基本组成_时钟周期_04

图中是我们实际系统里性能监测工具 NewRelic 中的响应时间,代表了每个外部的 Web 请求的执行时间。

吞吐率

吞吐率或者带宽,想要提升这个指标,你可以理解为让计算机“搬得更多”。

计算机基本组成_1024程序员节_05

服务器使用的网络带宽,通常就是一个吞吐率性能指标

吞吐率是指我们在一定的时间范围内,到底能处理多少事情。

性能,定义成响应时间的倒数,就是:性能 = 1/ 响应时间。

这样一来,响应时间越短,性能的数值就越大。同样一个程序,在 Intel 最新的 CPU Coffee Lake 上,只需要 30s 就能运行完成,而在 5 年前 CPU Sandy Bridge 上,需要 1min 才能完成。

3.2 计算机的计时单位:CPU时钟

虽然时间是一个很自然的用来衡量性能的指标,但是用时间来衡量时,有两个问题。

时间来衡量

第一个就是时间不“准”。如果用你自己随便写的一个程序,来统计程序运行的时间,每一次统计结果不会完全一样。有可能这一次花了 45ms,下一次变成了 53ms。

但是,计算机可能同时运行着好多个程序,CPU 实际上不停地在各个程序之间进行切换。在这些走掉的时间里面,很可能 CPU 切换去运行别的程序了。而且,有些程序在运行的时候,可能要从网络、硬盘去读取数据,要等网络和硬盘把数据读出来,给到内存和 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

其次,即使我们已经拿到了 CPU 时间,我们也不一定可以直接“比较”出两个程序的性能差异。即使在同一台计算机上,CPU 可能满载运行也可能降频运行,降频运行的时候自然花的时间会多一些。

除了CPU之外,时间这个性能指标还受到主板,内存,这些其他相关硬件的影响。

程序的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 时钟周期数 = 指令数×每条指令的平均时钟周期数(Cycles Per Instruction,简称 CPI)

不同的指令需要的 Cycles 是不同的,加法和乘法都对应着一条 CPU 指令,但是乘法需要的 Cycles 就比加法要多,自然也就慢。

程序的 CPU 执行时间 = 指令数×CPI×Clock Cycle Time

优化

  • 时钟周期时间,就是计算机主频,这个取决于计算机硬件。我们所熟知的摩尔定律就一直在不停地提高我们计算机的主频。比如说,我最早使用的 80386 主频只有 33MHz,现在手头的笔记本电脑就有 2.8GHz,在主频层面,就提升了将近 100 倍。
  • 每条指令的平均时钟周期数 CPI,就是一条指令到底需要多少 CPU Cycle。在后面讲解 CPU 结构的时候,我们会看到,现代的 CPU 通过流水线技术(Pipeline),让一条指令需要的 CPU Cycle 尽可能地少。因此,对于 CPI 的优化,也是计算机组成和体系结构中的重要一环。
  • 指令数,代表执行我们的程序到底需要多少条指令、用哪些指令。这个很多时候就把挑战交给了编译器。同样的代码,编译成计算机指令时候,就有各种不同的表示方式。

四.功耗

程序的 CPU 执行时间 = 指令数×CPI×Clock Cycle Time

这么来看,如果要提升计算机的性能,我们可以从指令数、CPI 以及 CPU 主频这三个地方入手。要搞定指令数或者 CPI,乍一看都不太容易。于是,研发 CPU 的硬件工程师们,从 80 年代开始,就挑上了 CPU 这个“软柿子”。在 CPU 上多放一点晶体管,不断提升 CPU 的时钟频率,这样就能让 CPU 变得更快,程序的执行时间就会缩短。

于是,从 1978 年 Intel 发布的 8086 CPU 开始,计算机的主频从 5MHz 开始,不断提升。1980 年代中期的 80386 能够跑到 40MHz,1989 年的 486 能够跑到 100MHz,直到 2000 年的奔腾 4 处理器,主频已经到达了 1.4GHz。而消费者也在这 20 年里养成了“看主频”买电脑的习惯。当时已经基本垄断了桌面 CPU 市场的 Intel 更是夸下了海口,表示奔腾 4 所使用的 CPU 结构可以做到 10GHz,颇有一点“大力出奇迹”的意思。

4.1 功耗:CPU 的“人体极限”

奔腾 4 的 CPU 主频从来没有达到过 10GHz,最终它的主频上限定格在 3.8GHz。这还不是最糟的,更糟糕的事情是,大家发现,奔腾 4 的主频虽然高,但是它的实际性能却配不上同样的主频。想要用在笔记本上的奔腾 4 2.4GHz 处理器,其性能只和基于奔腾 3 架构的奔腾 M 1.6GHz 处理器差不多。

于是,这一次的“大力出悲剧”,不仅让 Intel 的对手 AMD 获得了喘息之机,更是代表着“主频时代”的终结。后面几代 Intel CPU 主频不但没有上升,反而下降了。到如今,2019 年的最高配置 Intel i9 CPU,主频也只不过是 5GHz 而已。相较于 1978 年到 2000 年,这 20 年里 300 倍的主频提升,从 2000 年到现在的这 19 年,CPU 的主频大概提高了 3 倍。

计算机基本组成_计算机组成_06

功耗问题

我们的 CPU,一般都被叫作超大规模集成电路(Very-Large-Scale Integration,VLSI)。这些电路,实际上都是一个个晶体管组合而成的。CPU 在计算,其实就是让晶体管里面的“开关”不断地去“打开”和“关闭”,来组合完成各种运算和功能。

想要计算得快,一方面,我们要在 CPU 里,同样的面积里面,多放一些晶体管,也就是增加密度;另一方面,我们要让晶体管“打开”和“关闭”得更快一点,也就是提升主频。而这两者,都会增加功耗,带来耗电和散热的问题。

因此,在 CPU 里面,能够放下的晶体管数量和晶体管的“开关”频率也都是有限的。

功耗 ~= 1/2 ×负载电容×电压的平方×开关频率×晶体管数量

“制程”工艺, 从 28nm 到 7nm,相当于晶体管本身变成了原来的 1/4 大小。

计算机基本组成_响应时间_07

但是,功耗增加太多,就会导致 CPU 散热跟不上,这时,我们就需要降低电压。这里有一点非常关键,在整个功耗的公式里面,功耗和电压的平方是成正比的。这意味着电压下降到原来的 1/5,整个的功耗会变成原来的 1/25。

4.2 并行优化,理解阿姆达尔定律

于是,从奔腾 4 开始,Intel 意识到通过提升主频比较“难”去实现性能提升,边开始推出 Core Duo 这样的多核 CPU,通过提升“吞吐率”而不是“响应时间”,来达到目的。

计算机基本组成_执行时间_08

并行处理

  • 需要进行的计算,本身可以分解成几个可以并行的任务。好比上面的乘法和加法计算,几个人可以同时进行,不会影响最后的结果。
  • 需要能够分解好问题,并确保几个人的结果能够汇总到一起。
  • 在“汇总”这个阶段,是没有办法并行进行的,还是得顺序执行,一步一步来。

阿姆达尔定律(Amdahl’s Law)。这个定律说的就是,对于一个程序进行优化之后,处理器并行运算之后效率提升的情况。

优化后的执行时间 = 受优化影响的执行时间 / 加速倍数 + 不受影响的执行时间

4.3 原则性的性能提升方法

  • 加速大概率事件。
  • 通过流水线提高性能,我们的 CPU 其实就是一个“运算工厂”。我们把 CPU 指令执行的过程进行拆分,细化运行,也是现代 CPU 在主频没有办法提升那么多的情况下,性能仍然可以得到提升的重要原因之一。
  • 通过预测提高性能。通过预先猜测下一步该干什么,而不是等上一步运行的结果,提前进行运算,也是让程序跑得更快一点的办法。

参考

《深入浅出计算机组成原理》

公众号

计算机基本组成_1024程序员节_09
名称:大数据计算
微信号:bigdata_limeng