# 4.5.10 未完成的工作

我们已经创建了 PIPE 流水线化的微处理器结构,设计了控制逻辑块,并实现了处理普通流水线流不足以处理的特殊情况的流水线控制逻辑。不过,PIPE 还是缺乏一些实际微处理器设计中所必需的关键特性。我们会强调其中一些,并讨论要增加这些特性需要些什么。

# 1. 多周期指令

Y86-64 指令集中的所有指令都包括一些简单的操作,例如数字加法。这些操作可以在执行阶段中一个周期内处理完。在一个更完整的指令集中,我们还将实现一些需要更为复杂操作的指令,例如,整数乘法和除法,以及浮点运算。在一个像 PIPE 这样性能中等的处理器中,这些操作的典型执行时间从浮点加法的 3 或 4 个周期到整数除法的 64 个周期。为了实现这些指令,我们既需要额外的硬件来执行这些计算,还需要一种机制来协调这些指令的处理与流水线其他部分之间的关系。

实现多周期指令的一种简单方法就是简单地扩展执行阶段逻辑的功能,添加一些整数和浮点算术运算单元。一条指令在执行阶段中逗留它所需要的多个时钟周期,会导致取指和译码阶段暂停。这种方法实现起来很简单,但是得到的性能并不是太好。

通过采用独立于主流水线的特殊硬件功能单元来处理较为复杂的操作,可以得到更好的性能。通常,有一个功能单元来执行整数乘法和除法,还有一个来执行浮点操作。当一条指令进入译码阶段时,它可以被发射到特殊单元。在这个特殊单元执行该操作时,流水线会继续处理其他指令。通常,浮点单元本身也是流水线化的,因此多条指令可以在主流水线和各个单元中并发执行。

不同单元的操作必须同步,以避免出错。比如说,如果在不同单元执行的各个指令之间有数据相关,控制逻辑可能需要暂停系统的某个部分,直到由系统其他某个部分处理的操作的结果完成。经常使用各种形式的转发,将结果从系统的一个部分传递到其他部分,这和前面 PIPE 各个阶段之间的转发一样。虽然与 PIPE 相比,整个设计变得更为复杂,但还是可以使用暂停、转发以及流水线控制等同样的技术来使整体行为与顺序的 ISA 模型相匹配。

# 2. 与存储系统的接口

在对 PIPE 的描述中,我们假设取指单元和数据内存都可以在一个时钟周期内读或是写内存中任意的位置。我们还忽略了由自我修改代码造成的可能冒险,在自我修改代码中,一条指令对一个存储区域进行写,而后面又从这个区域中读取指令。进一步说,我们是以存储器位置的虚拟地址来引用它们的,这要求在执行实际的读或写操作之前,要将虚拟地址翻译成物理地址。显然,要在一个时钟周期内完成所有这些处理是不现实的。更糟糕的是,要访问的存储器的值可能位于磁盘上,这会需要上百万个时钟周期才能把数据读入到处理器内存中。

正如将在第 6 章和第 9 章中讲述的那样,处理器的存储系统是由多种硬件存储器和管理虚拟内存的操作系统软件共同组成的。存储系统被组织成一个层次结构,较快但是较小的存储器保持着存储器的一个子集,而较慢但是较大的存储器作为它的后备。最靠近处理器的一层是高速缓存(cache)存储器,它提供对最常使用的存储器位置的快速访问。一个典型的处理器有两个第一层高速缓存——一个用于读指令,一个用于读和写数据。另一种类型的高速缓存存储器,称为翻译后备缓冲器(Translation Look-aside Buffer,TLB),它提供了从虚拟地址到物理地址的快速翻译。将 TLB 和高速缓存结合起来使用,在大多数时候,确实可能在一个时钟周期内读指令并读或是写数据。因此,我们的处理器对访问存储器的简化看法实际上是很合理的。

虽然高速缓存中保存有最常引用的存储器位置,但是有时候还会出现高速缓存不命中(miss),也就是有些引用的位置不在高速缓存中。在最好的情况中,可以从较高层的高速缓存或处理器的主存中找到不命中的数据,这需要 3~20 个时钟周期。同时,流水线会简单地暂停,将指令保持在取指或访存阶段,直到高速缓存能够执行读或写操作。至于流水线设计,通过添加更多的暂停条件到流水线控制逻辑,就能实现这个功能。高速缓存不命中以及随之而来的与流水线的同步都完全是由硬件来处理的,这样能使所需的时间尽可能地缩短到很少数量的时钟周期。

在有些情况中,被引用的存储器位置实际上是存储在磁盘存储器上的。此时,硬件会产生一个缺页(page fault)异常信号。同其他异常一样,这个异常会导致处理器调用操作系统的异常处理程序代码。然后这段代码会发起一个从磁盘到主存的传送操作。一旦完成,操作系统会返回到原来的程序,而导致缺页的指令会被重新执行。这次,存储器引用将成功,虽然可能会导致高速缓存不命中。让硬件调用操作系统例程,然后操作系统例程又会将控制返回给硬件,这就使得硬件和系统软件在处理缺页时能协同工作。因为访问磁盘需要数百万个时钟周期,OS 缺页中断处理程序执行的处理所需的几百个时钟周期对性能的影响可以忽略不计。

从处理器的角度来看,将用暂停来处理短时间的高速缓存不命中和用异常处理来处理长时间的缺页结合起来,能够顾及到存储器访问时由于存储器层次结构引起的所有不可预测性。

旁注 当前的微处理器设计

一个五阶段流水线,例如已经讲过的 PIPE 处理器,代表了 20 世纪 80 年代中期的处理器设计水平。Berkeley 的 Patterson 研究组开发的 RISC 处理器原型是第一个 SPARC 处理器的基础,它是 Sun Microsystems 在 1987 年开发的。Stanford 的 Hennessy 研究组开发的处理器由 MIPS Technologies(一个由 Hennessy 成立的公司)在 1986 年商业化了。这两种处理器都使用的是五阶段流水线。Intel 的 i486 处理器用的也是五阶段流水线,只不过阶段之间的职责划分不太一样,它有两个译码阶段和一个合并的执行/访存阶段 [27]。

这些流水线化的设计的吞吐量都限制在最多一个时钟周期一条指令。4.5.9 小节中描述的 CPI(Cycles Per Instruction,每指令周期)测量值不可能小于 1.0。不同的阶段一次只能处理一条指令。较新的处理器支持超标量(superscalar)操作,意味着它们通过并行地取指、译码和执行多条指令,可以实现小于 1.0 的 CPI。当超标量处理器已经广泛使用时,性能测量标准已经从 CPI 转化成了它的倒数——每周期执行指令的平均数,即 IPC。对超标量处理器来说,IPC 可以大于 1.0。最先进的设计使用了一种称为乱序(out-of-order)执行的技术来并行地执行多条指令,执行的顺序也可能完全不同于它们在程序中出现的顺序,但是保留了顺序 ISA 模型蕴含的整体行为。作为对程序优化的讨论的一部分,我们将会在第 5 章中讨论这种形式的执行。

不过,流水线化的处理器并不只有传统的用途。现在出售的大部分处理器都用在嵌入式系统中,控制着汽车运行、消费产品,以及其他一些系统用户不能直接看到处理器的设备。在这些应用中,与性能较高的模型相比,流水线化的处理器的简单性(比如说像我们在本章中讨论的这样)会降低成本和功耗需求。

最近,随着多核处理器受到追捧,有些人声称通过在一个芯片上集成许多简单的处理器,比使用少量更复杂的处理器能获得更多的整体计算能力。这种策略有时被称为“多核”处理器 [10]。