指令流水线的基本概念

前五节讲的是”怎样把一条指令做对”。从这一节开始,问题换成”怎样把很多条指令做快”——这是5.1.1 立的第二条主线的起点。

做对和做快是两个独立的问题,这一点必须先立住:流水线不改变任何一条指令的语义,也不改变任何一条指令的执行结果。它改变的只有一件事——同一时刻有多少条指令正在被处理。

机制

从”串行”到”重叠”

5.2.3 已经把一条指令切成了若干阶段。非流水线的做法是:一条指令走完全部阶段,下一条才开始。

指令1: [取指][译码][执行][访存][写回]
指令2:                               [取指][译码][执行][访存][写回]

每一时刻只有一个部件在工作,其余四个全部闲着。 这是纯粹的浪费——取指部件在译码阶段完全没事干。

流水线的做法:让每个部件一直有活干。

指令1: [取指][译码][执行][访存][写回]
指令2:       [取指][译码][执行][访存][写回]
指令3:             [取指][译码][执行][访存][写回]
指令4:                   [取指][译码][执行][访存][写回]

从第 5 拍开始,五个部件同时在为五条不同的指令工作。

这就是流水线的全部思想:不是让单件事变快,而是让所有工位同时开工。工厂装配线是同一个道理——一辆车的组装时间没变,但下线速度变成了每个工位周期一辆。

吞吐率与延迟:必须分开的两个量

这是本节最需要焊死的区分,5.6 后面四节全部依赖它。

延迟(Latency)吞吐率(Throughput)
定义一条指令从进入到完成的时间单位时间完成的指令条数
流水线后不变,甚至略增(多了段间寄存器的延迟)提高约 倍( 为段数)
对应公式里的单条指令的执行时间
流水线提高吞吐率,不缩短单条指令的延迟

为什么延迟反而会略增:切成 段后,每段之间要插一个段间寄存器(也叫流水寄存器),它有自己的建立时间和传输延迟。所以

寄存器

单条指令走完 段的总时间,比不流水时还长一点点。 但因为每拍都有一条指令完成,平均下来快了。

这个区分是很多说法的判据来源:

  • “流水线使指令执行速度提高了 5 倍” → 说法不严谨,提高的是吞吐率
  • “流水线降低了 CPI” → 对,理想情况下 CPI 从 降到 1
  • “流水线缩短了程序执行时间” → 对,因为程序有很多条指令

时空图

横轴时间、纵轴指令,是流水线所有计算题的作图工具。

指令 \ 时钟12345678
I1IFIDEXMEMWB
I2IFIDEXMEMWB
I3IFIDEXMEMWB
I4IFIDEXMEMWB

三段读法:

阶段时钟范围特征
建立(填充)第 1 ~ 拍流水线还没装满,部分段空闲
稳定(满载)第 ~ 拍每拍完成一条指令
排空最后 拍不再有新指令进入
总时间第一条走完其余每条拍(单位:)

这个式子是 5.6.4 全部性能公式的起点,必须能直接写出来。

流水线的分类

四组分类,考试主要考后两组的辨析。

分类依据类别说明
层次部件级(运算操作流水线)把一次浮点加分成对阶、尾数加、规格化等段
处理机级(指令流水线)本节讨论的就是它
处理机间级(宏流水线)多个处理机各处理一道工序
功能数单功能流水线只能完成一种固定功能
多功能流水线各段可重新连接以完成不同功能
连接方式静态流水线同一时间只能按一种功能连接
动态流水线同一时间可按多种功能连接,各段可同时为不同功能服务
是否有反馈线性流水线每段只经过一次
非线性流水线有反馈回路,某段可被多次使用

静态 / 动态只对多功能流水线才有意义——单功能流水线只有一种连法,无从”静”或”动”。这是这组分类最容易被问的一点。

“动态流水线”和后面 5.6.5 的”动态调度(乱序执行)“是两个不同的概念,中文里都带”动态”,容易串。前者说的是各段能否同时服务于不同功能,后者说的是硬件能否按运行时依赖打乱指令顺序。

流水线成立的三个条件

不是任何任务都能流水化,需要满足:

  1. 各段的时间应尽量相等——否则 被最慢段绑架,快的段大量空闲
  2. 各段之间要有缓冲(段间寄存器)——存放上一段的结果,供下一段使用
  3. 各段的功能部件必须独立——不能两段共用同一个部件,否则每拍都会冲突

第 1 条是”要整齐就得按最慢的来”在本章的第四次出现(前三次见 5.4.2)。它是RISC 设计的直接动机:指令定长、格式规整、只有 Load/Store 访存——这三条都是为了让五段的时间尽量接近。

第 3 条是结构冒险的来源,见 5.6.3。它也解释了为什么流水线 CPU 必须用专用数据通路而不是总线结构(5.3.3):总线是全局共享的,天然违反”各段独立”。

指令集对流水线的要求

这一节是 4.4.2 那三条 RISC 特征的收口。

RISC 的特征对流水线的好处
定长指令IF 段时间固定;PC 增量是常数,不必等译码(4.1.3)
格式规整、寄存器号位置固定ID 段可以在译码的同时就去读寄存器,不必先知道是哪条指令
只有 Load/Store 访存只有一个段(MEM)需要访存,各段时间容易拉齐
寻址方式少地址计算统一放在 EX 段,不需要额外的间址周期

第二条最微妙:正因为寄存器号总在同一位置,ID 段可以并行地做两件事——译码器在看操作码的同时,寄存器堆已经按固定位置的字段去取数了。若指令格式不规整,就必须先译码知道是哪条指令、寄存器号在哪,才能去取——ID 段就要被拆成两拍。

这正是 4.1.4 那条主线的最终收口:变长编码省了空间,代价是”必须先译码才知道下一步做什么”,而在流水线上,这个代价直接表现为段数变多、或者某一段被迫串行化。

边界

边界辨析:吞吐率 / 延迟

  • 吞吐率:单位时间完成多少条指令 → 流水线提高它
  • 延迟:一条指令从头到尾的时间 → 流水线不缩短它,甚至略增

说”流水线加快了指令的执行速度”是含糊的;准确说法是加快了指令的完成速率。

边界辨析:静态流水线 / 动态流水线

只对多功能流水线有意义:

  • 静态:同一时刻只能按一种功能连接,换功能要等流水线排空
  • 动态:同一时刻可按多种功能连接

与 5.6.5 的”动态调度 / 乱序执行”不是一回事,只是中文都带”动态”。

边界辨析:流水线的段数 / 指令周期的阶段数

5.2.1 的四个阶段(取指/间址/执行/中断)是逻辑划分,与流水线的五段(IF/ID/EX/MEM/WB)不是同一套划分。

流水线的分段依据是”各段时间尽量相等且部件独立”,与逻辑阶段无关。别把两套名字混用。

对照速查

说法判断
流水线提高了吞吐率✅
流水线缩短了单条指令的执行时间❌ 甚至略增
流水线降低了 CPI✅ 理想情况下降到 1
段数越多性能越好❌ 段间寄存器开销 + 冒险代价,见 5.6.4
静态流水线可以同时按多种功能连接❌ 那是动态流水线
单功能流水线分静态和动态❌ 只有多功能才分
流水线各段时间应尽量相等✅ 三个成立条件之一
流水线需要段间寄存器✅

考点

  • 吞吐率与延迟的分辨:几乎每年都以某种形式出现。
  • 画时空图:横轴时钟、纵轴指令,注意填充与排空。
  • 总时间公式 :一切性能计算的起点。
  • 静态 / 动态流水线辨析:只对多功能流水线有意义。
  • 流水线成立的三个条件:段时间相等、有缓冲、部件独立。
  • RISC 的哪些特征有利于流水:定长、规整、Load/Store、寻址方式少,每条都要能说出对应的好处。

链接