第 1 章 引言 -- 1.1 计算机体系结构的研究内容
要研究怎么造计算机,硬件方面要理解计算机组成原理和计算机体系结构,软件方面要理解操作系统和编译原理。计算机体系结构就是研究怎么做CPU的核心课程。信息产业的主要技术平台都是以中央处理器(Central Processing Unit,简称CPU)和操作系统(Operating System,简称OS)为核心构建起来的,如英特尔公司的X86架构CPU和微软公司的Windows操作系统构成的Wintel平台,ARM公司的ARM架构CPU和谷歌公司的Android操作系统构成的“AA”平台。龙芯正在致力于构建独立于Wintel和AA体系的第三套生态体系。
1.1 计算机体系结构的研究内容
计算机体系结构研究内容涉及的领域非常广泛,纵向以指令系统结构和CPU的微结构为核心,向下到晶体管级的电路结构,向上到应用程序编程接口(Application Programming Interface,简称API);横向以个人计算机和服务器的体系结构为核心,低端到手持移动终端和微控制器(Micro-Controller Unit,简称MCU)的体系结构,高端到高性能计算机(High Performance Computer,简称HPC)的体系结构。
1.1.1 一以贯之
为了说明计算机体系结构研究涉及的领域,我们看一个简单的问题:为什么按一下键盘,PPT会翻页?这是一个怎样的过程?在这个过程中,应用程序(WPS)、操作系统(Windows或Linux)、硬件系统、CPU、晶体管是怎么协同工作的?
下面介绍用龙芯CPU构建的系统实现上述功能的原理性过程。
按一下键盘,键盘会产生一个信号送到南桥芯片,南桥芯片把键盘的编号保存在南桥内部的一个寄存器中,并向处理器发送一个外部中断信号。该外部中断信号传到CPU内部后把CPU中一个控制寄存器的某一个位置置为“1”。CPU中另外一个控制寄存器有屏蔽位来确定是否处理这个外部中断信号。
屏蔽处理后的中断信号被附在一条译码后的指令上送到重排序缓冲(Re-Order Buffer,简称ROB)。外部中断是例外(Exception,也称“异常”)的一种,发生例外的指令不会被送到功能部件执行。当这条指令成为重排序缓冲的第一条指令时CPU处理例外。重排序缓冲为了给操作系统一个精确的例外现场,处理例外前要把例外指令前面的指令都执行完,后面的指令都取消掉。
重排序缓冲向所有的模块发出一个取消信号,取消该指令后面的所有指令;修改控制寄存器,把系统状态设为核心态;保存例外原因、发生例外的程序计数器(Program Counter,简称PC)等到指定的控制寄存器中;然后把程序计数器的值置为相应的例外处理入口地址进行取指(LoongArch中例外的入口地址计算规则可以参见其体系结构手册)。
处理器跳转到相应的例外处理器入口后执行操作系统代码,操作系统首先保存处理器现场,包括寄存器内容等。保存现场后,操作系统向CPU的控制寄存器读例外原因,发现是外部中断例外,就向南桥的中断控制器读中断原因,读的同时清除南桥的中断位。读回来后发现中断原因是有人敲了空格键。
操作系统接下来要查找读到的空格是给谁的:有没有进程处在阻塞状态等键盘输入。大家都学过操作系统的进程调度,知道进程至少有三个状态:运行态、阻塞态、睡眠态,进程在等IO输入时处在阻塞态。操作系统发现有一个名为WPS的进程处于阻塞态,这个进程对空格键会有所响应,就把WPS唤醒。
WPS被唤醒后处在运行状态。发现操作系统传过来的数据是个键盘输入空格,表示要翻页。WPS就把下一页要显示的内容准备好,调用操作系统中的显示驱动程序,把要显示的内容送到显存,由图形处理器(Graphic Processing Unit,简称GPU)通过访问显存空间刷新屏幕。达到了翻一页的效果。
再看一个问题:如果在翻页的过程中,发现翻页过程非常卡顿,即该计算机在WPS翻页时性能较低,可能是什么原因呢?首先得看看系统中有没有其他任务在运行,如果有很多任务在运行,这些任务会占用CPU、内存带宽、IO带宽等资源,使得WPS分到的资源不够,造成卡顿。如果系统中没有其他应用与WPS抢资源,还会卡顿,那是什么原因呢?多数人会认为是CPU太慢,需要升级。实际上,在WPS翻页时,CPU干的活不多。一种可能是下一页包含很多图形,尤其是很多矢量图,需要GPU画出来,GPU忙不过来了。另外一种可能是要显示的内容数据量大,要把大量数据从WPS的应用程序空间传给GPU使用的专门空间,内存带宽不足导致不能及时传输。在独立显存的情况下,数据如何从内存传输到显存有两种不同的机制:由CPU从内存读出来再写到显存需要CPU具有专门的IO加速功能,因为显存一般是映射在CPU的IO空间;不通过CPU,通过直接内存访问(Direct Memory Access,简称DMA)的方式直接从内存传输到显存会快得多。
“计算机体系结构”课程是研究怎么造计算机,而不是怎么用计算机。我们不是学习驾驶汽车,而是学习如何造汽车。一个计算机体系结构设计人员就像一个带兵打仗的将领,要学会排兵布阵。要上知天文、下知地理,否则就不会排兵布阵,或者只会纸上谈兵地排兵布阵,只能贻误军国大事。对计算机体系结构设计来说,“排兵布阵”就是体系结构设计,“上知天文”就是了解应用程序、操作系统、编译器的行为特征,“下知地理”就是了解逻辑、电路、工艺的特点。永远不要就体系结构论体系结构,要做到应用、系统、结构、逻辑、电路、器件的融会贯通。就像《论语》中说的“吾道一以贯之”。
图1.1给出了常见通用计算机系统的结构层次图。该图把计算机系统分成应用程序、操作系统、硬件系统、晶体管四个大的层次。注意把这四个层次联系起来的三个界面。第一个界面是应用程序编程接口API(Application Programming Interface),也可以称作“操作系统的指令系统”,介于应用程序和操作系统之间。API是应用程序的高级语言编程接口,在编写程序的源代码时使用。常见的API包括C语言、Fortran语言、Java语言、JavaScript语言接口以及OpenGL图形编程接口等。使用一种API编写的应用程序经重新编译后可以在支持该API的不同计算机上运行。所有应用程序都是通过API编出来的,在IT产业,谁控制了API谁就控制了生态,API做得好,APP(Application)就多。API是建生态的起点。第二个界面是指令系统ISA(Instruction Set Architecture),介于操作系统和硬件系统之间。常见的指令系统包括X86、ARM、MIPS、RISC-V和LoongArch等。指令系统是实现目标码兼容的关键,由于IT产业的主要应用都是通过目标码的形态发布的,因此ISA是软件兼容的关键,是生态建设的终点。指令系统除了实现加减乘除等操作的指令外,还包括系统状态的切换、地址空间的安排、寄存器的设置、中断的传递等运行时环境的内容。第三个界面是工艺模型,介于硬件系统与晶体管之间。工艺模型是芯片生产厂家提供给芯片设计者的界面,除了表达晶体管和连线等基本参数的SPICE(Simulation Program with Integrated Circuit Emphasis)模型外,该工艺所能提供的各种IP也非常重要,如实现PCIE接口的物理层(简称PHY)等。
需要指出的是,在API和ISA之间还有一层应用程序二进制接口(Application Binary Interface,简称ABI)。ABI是应用程序访问计算机硬件及操作系统服务的接口,由计算机的用户态指令和操作系统的系统调用组成。为了实现多进程访问共享资源的安全性,处理器设有“用户态”与“核心态”。用户程序在用户态下执行,操作系统向用户程序提供具有预定功能的系统调用函数来访问只有核心态才能访问的硬件资源。当用户程序调用系统调用函数时,处理器进入核心态执行诸如访问IO设备、修改处理器状态等只有核心态才能执行的指令。处理完系统调用后,处理器返回用户态执行用户代码。相同的应用程序二进制代码可以在相同ABI的不同计算机上运行。
学习计算机体系结构的人一定要把图1.1装在心中。从一般意义上说,计算机体系结构的研究内容包括指令系统结构、硬件系统结构和CPU内部的微结构。但做体系结构设计而上不懂应用和操作系统,下不懂晶体管级行为,就像带兵打仗排兵布阵的人不知天文、不晓地理,是做不好体系结构的。首先,指令系统就是从应用程序算法中抽取出来的“算子”。只有对应用程序有深入的了解,才能决定哪些事情通过指令系统由硬件直接实现,哪些事情通过指令组合由软件实现。其次,硬件系统和CPU的微结构要针对应用程序的行为进行优化。如针对媒体处理等流式应用,需要通过预取提高性能;CPU的高速缓存就是利用了应用程序访存的局部性;CPU的转移猜测算法就是利用了应用程序转移行为的重复性和相关性;CPU的内存带宽设计既要考虑CPU本身的访存需求,也要考虑由显示引起的GPU访问内存的带宽需求。再次,指令系统和CPU微结构的设计要充分考虑操作系统的管理需求。如操作系统通过页表进行虚存管理需要CPU实现TLB(Translation Lookaside Buffer)对页表进行缓存并提供相应的TLB管理指令;CPU实现多组通用寄存器高速切换的机制有利于加速多线程切换;CPU实现多组控制寄存器和系统状态的高速切换机制有利于加速多操作系统切换。最后,计算机中主要的硬件实体如CPU、GPU、南北桥、内存等都是通过晶体管来实现的,只有对晶体管行为有一定的了解才能在结构设计阶段对包括主频、成本、功耗在内的硬件开销进行评估。如高速缓存的容量是制约CPU主频和面积的重要因素,多发射结构的发射电路是制约主频的重要因素,在微结构设计时都是进行权衡取舍的重要内容。
1.1.2 什么是计算机
什么是计算机?大多数人认为计算机就是我们桌面的电脑,实际上计算机已经深入到我们信息化生活的方方面面。除了大家熟知的个人电脑、服务器和工作站等通用计算机外,像手机、数码相机、数字电视、游戏机、打印机、路由器等设备的核心部件都是计算机,都是计算机体系结构研究的范围。也许此刻你的身上就有好几台计算机。
看几个著名的计算机应用的例子。比如说美国国防部有一个ASCI(Accelerated Strategic Computing Initiative)计划,为核武器模拟制造高性能计算机。20世纪90年代,拥有核武器的国家签订了全面禁止核试验条约,凡是签这个条约的国家都不能进行核武器的热试验,或者准确地说不能做“带响”的核武器试验。这对如何保管核武器提出了挑战,核武器放在仓库里不能做试验,这些核武器放了一百年以后,拿出来还能不能用?会不会放着放着自己炸起来?想象一下一块铁暴露在空气中一百年会锈成什么样子。这就需要依靠计算机模拟来进行核武器管理,核武器的数字模拟成为唯一可以进行的核试验,这种模拟需要极高性能的计算机。据美国国防部估计,为了满足2010年核管理的需要,需要每秒完成1016~1017次运算的计算机。现在我们桌面电脑的频率在1GHz的量级(词头“G”表示
109),加上向量化、多发射和多核的并行,现在的先进通用CPU性能大约在1011的运算量级,即每秒千亿次运算,10^16运算量级就需要10万个CPU,耗电几十兆瓦。美国在2008年推出的世界上首台速度达到PFLOPS(每秒千万亿次运算,其中词头“P”表示
10^15,FLOPS表示每秒浮点运算次数)的高性能计算机Roadrunner就用于核模拟。高性能计算机的应用还有很多。例如波音777是第一台完全用计算机模拟设计出来的飞机,还有日本的地球模拟器用来模拟整个地球的地质活动以进行地震方面的研究。高性能计算已经成为除了科学实验和理论推理外的第三种科学研究手段。
计算机的另外一个极端应用就是手机,手机也是计算机的一种。现在的手机里至少有一个CPU,有的甚至有几个。
希望大家建立一个概念,计算机不光是桌面上摆的个人计算机,它可以大到一个厅都放不下,需要专门为它建一个电站来供电,也可以小到揣在我们的兜里,充电两个小时就能用一整天。不管这个计算机的规模有多大,都是计算机体系结构的研究对象。计算机是为了满足人们各种不同的计算需求设计的自动化计算设备。随着人类科技的进步和新需求的提出,最快的计算机会越来越大,最小的计算机会越来越小。
1.1.3 计算机的基本组成
我们从小就学习十进制的运算,0、1、2、3、4、5、6、7、8、9十个数字,逢十进一。计算机中使用二进制,只有0和1两个数字,逢二进一。为什么用二进制,不用我们习惯的十进制呢?因为二进制最容易实现。自然界中二值系统非常多,电压的高低、水位的高低、门的开关、电流的有无等等都可以组成二值系统,都可以用来做计算机。二进制最早是由莱布尼茨发明的,冯·诺依曼最早将二进制引入计算机的应用,而且计算机里面的程序和数据都用二进制。从某种意义上说,中国古人的八卦也是一种二进制。
计算机的组成非常复杂,但其基本单元非常简单。打开一台PC的机箱,可以发现电路板上有很多芯片。如图1.2所示,一个芯片就是一个系统,由很多模块组成,如加法器、乘法器等;而一个模块由很多逻辑门组成,如非门、与门、或门等;逻辑门由晶体管组成,如PMOS管和NMOS管等;晶体管则通过复杂的工艺过程形成。所以计算机是一个很复杂的系统,由很多可以存储和处理二进制运算的基本元件组成。就像盖房子一样,再宏伟、高大的建筑都是由基本的砖瓦、钢筋水泥等材料搭建而成的。在CPU芯片内部,一根头发的宽度可以并排走上千根导线;购买一粒大米的钱可以买上千个晶体管。
现在计算机结构的基本思想是1945年匈牙利数学家冯·诺依曼结合EDVAC计算机的研制提出的,因此被称为冯·诺依曼结构。
我们通过一个具体的例子来介绍冯·诺依曼结构。比如说求式子(3×4+5×7)的值,人类是怎么计算的呢?先计算3×4=12,把12记在脑子里,接着计算5×7=35,再计算12+35=47。我们在计算过程中计算和记忆(存储)都在一个脑袋里(但式子很长的时候需要把临时结果记在纸上)。
计算机的计算和记忆是分开的,负责计算的部分由运算器和控制器组成,称为中央处理器,就是CPU;负责记忆的部分称为存储器。存储器里存了两样东西,一是存了几个数,3、4、5、7、12、35、47,这个叫作数据;二是存储了一些指令。也就是说,操作对象和操作序列都保存在存储器里。
我们来看看计算机是如何完成(3×4+5×7)的计算的。计算机把3、4、5、7这几个数都存在内存中,计算过程中的临时结果(12、35)和最终结果(47)也存在内存中;此外,计算机还把对计算过程的描述(程序)也存在内存中,程序由很多指令组成。表1.1a给出了内存中在开始计算前数据和指令存储的情况,假设数据存在100号单元开始的区域,程序存在200号单元开始的区域。
100 | 3 | 3 |
---|---|---|
101 | 4 | 4 |
102 | 5 | 5 |
103 | 7 | 7 |
104 | 12 | |
105 | 35 | |
106 | 47 | |
... | ... | |
200 | 读取100号单元 | 读取100号单元 |
201 | 读取101号单元 | 读取101号单元 |
202 | 两数相乘 | 两数相乘 |
203 | 存入结果到104号单元 | 存入结果到104号单元 |
204 | 读取102号单元 | 读取102号单元 |
205 | 读取103号单元 | 读取103号单元 |
206 | 两数相乘 | 两数相乘 |
207 | 存入结果到105号单元 | 存入结果到105号单元 |
208 | 读取104号单元 | 读取104号单元 |
209 | 读取1005号单元 | 读取105号单元 |
210 | 两数相加 | 两数相加 |
211 | 存入结果到106号单元 | 存入结果到106号单元 |
a) | b) |
计算机开始运算过程如下:CPU从内存200号单元取回第一条指令,这条指令就是“读取100号单元”,根据这条指令的要求从内存把“3”读进来;再从内存201号单元取下一条指令“读取101号单元”,然后根据这条指令的要求从内存把“4”读进来;再从内存202号单元取下一条指令“两数相乘”,乘出结果为“12”;再从内存203号单元取下一条指令“存入结果到104号单元”,把结果“12”存入104号单元。如此往复直到程序结束。表1.1b是程序执行结束时内存的内容。
大家看看刚才这个过程,比我们大脑运算烦琐多了。我们大脑算三步就算完了,而计算机需要那么多步,又取指令又取数据,挺麻烦的。这就是冯·诺依曼结构的基本思想:数据和程序都在存储器中,CPU从内存中取指令和数据进行运算并把结果也放到内存中。把指令和数据都存在内存中可以让计算机按照事先规定的程序自动地完成运算,是实现图灵机的一种简单方法。冯·诺依曼结构很好地解决了自动化的问题:把程序放在内存里,一条条取进来,自己就做起来了,不用人来干预。如果没有这样一种自动执行的机制,让人去控制计算机做什么运算,拨一下开关算一下,程序没有保存在内存中而是保存在人脑中,就成算盘了。计算机的发展日新月异,但70多年过去了还是使用冯·诺依曼结构。尽管冯·诺依曼结构有很多缺点,例如什么都保存在内存中使访存成为性能瓶颈,但我们还是摆脱不了它。
虽然经过了长期的发展,以存储程序和指令驱动执行为主要特点的冯·诺依曼结构仍是现代计算机的主流结构。笔者面试研究生的时候经常问一个问题:冯·诺依曼结构最核心的思想是什么?结果很多研究生都会答错。有人说是由计算器、运算器、存储器、输入、输出五个部分组成;有人说是程序计数器导致串行执行;等等。实际上,冯·诺依曼结构就是数据和程序都存在存储器中,CPU从内存中取指令和数据进行运算,并且把结果也放在内存中。概括起来就是存储程序和指令驱动执行。