Skip to content
TechMind

7 、指令流水线


一、流水线基本原理🧀

流水线把一条指令的执行过程拆成若干阶段,让多条指令在不同阶段重叠执行。经典五级流水线包括:

阶段 名称 主要工作
IF Instruction Fetch 取指并更新 PC
ID Instruction Decode 译码、读取寄存器
EX Execute ALU 运算或计算有效地址
MEM Memory Access 访问数据存储器
WB Write Back 把结果写回寄存器

流水线的主要目标是提高吞吐量,并不会把单条指令本身的逻辑工作量消除。

1、理想流水线性能🧀

设流水线有 \(k\) 段,共执行 \(n\) 条指令,每段耗时均为一个时钟周期 \(T\)。不考虑任何停顿时:

\[ \boxed{\text{总周期数}=k+n-1} \]
\[ \boxed{T_{total}=(k+n-1)T} \]
\[ \boxed{TP=\frac{n}{(k+n-1)T}=\frac{n}{k+n-1}f} \]

若非流水方式顺序执行每条指令的 \(k\) 个阶段,理想加速比为:

\[ \boxed{S=\frac{nk}{k+n-1}} \]

当 \(n\gg k\) 时,理想吞吐率趋近于每周期一条指令,加速比趋近于流水段数 \(k\)。

实际时钟周期由最慢流水段加流水寄存器开销决定。各段耗时越不均衡,流水线效率越低。


二、流水线冲突🧀

导致下一条指令不能在预定周期进入下一阶段的情况称为流水线冲突或冒险。

类型 原因 常见处理方法
结构冲突 多条指令同时争用同一硬件资源 增加资源、分离指令与数据存储器、停顿
数据冲突 指令之间存在数据依赖 转发、停顿、编译器调度
控制冲突 分支或跳转改变 PC 分支预测、延迟槽、停顿、冲刷流水线

1、数据相关🧀

设 R 表示读取寄存器,W 表示写寄存器:

指令 目的寄存器 源寄存器 含义
add rd, rs1, rs2 rd = W rs1 = R、rs2 = R 两个源、一个目的
lw rt, offset(rs) rt = W rs = R 内存读到寄存器
sw rt, offset(rs) 无 rt = R、rs = R 寄存器写入内存
beq rs1, rs2, label 无 rs1 = R、rs2 = R 比较后决定是否转移

常见数据相关有:

  • RAW(Read After Write):后续指令要读取前一条尚未写回的结果,属于真正的数据依赖。
  • WAR(Write After Read):后续指令写入前一条尚未读取的位置。
  • WAW(Write After Write):两条指令写入同一位置,完成顺序必须正确。

经典顺序发射、顺序完成的五级流水线主要遇到 RAW;乱序执行处理器还必须解决 WAR 和 WAW,通常使用寄存器重命名。

2、转发与停顿🧀

转发把某个流水段刚产生的结果直接送到后续指令需要它的输入端,不必等待结果写回寄存器堆。

情况 典型处理
ALU 指令产生结果,下一条 ALU 指令立即使用 从 EX/MEM 转发,通常不需要停顿
lw 后一条指令立即使用加载结果 停 1 拍、向 EX 注入 1 个气泡,再转发

发生 load-use 冲突并停顿时:

  • 消费者指令保持在 ID。
  • 后续指令保持在 IF,PC 暂停更新。
  • 控制逻辑向 EX 注入一个 NOP,形成气泡。

Note

“转发后 ALU 相关不需要停顿、load-use 停一拍”是经典五级流水线的结论。若具体处理器的阶段划分、存储器时序或分支处理位置不同,停顿周期也会不同。

3、流水线时序例题🧀

下面统一假设使用经典 IF-ID-EX-MEM-WB 五级流水线,并且具备完整的 ALU 结果转发。表中的同一列表示同一个时钟周期。

(1)连续 RAW:转发后不需要停顿🧀

1
2
3
add x5, x1, x2    # x5 <- x1 + x2
sub x6, x5, x3    # 读取上一条刚产生的 x5
and x7, x6, x4    # 读取上一条刚产生的 x6

第二条对 x5、第三条对 x6 都存在 RAW 相关。不过 ALU 结果在 EX 结束时已经产生,可以直接转发给下一条指令的 EX 输入端,所以不必插入气泡。

指令 / 周期 1 2 3 4 5 6 7
add x5, x1, x2 IF ID EX MEM WB — —
sub x6, x5, x3 — IF ID EX MEM WB —
and x7, x6, x4 — — IF ID EX MEM WB

转发路径为:

  • 第 4 周期:把 add 的结果转发给 sub 的 EX 阶段。
  • 第 5 周期:把 sub 的结果转发给 and 的 EX 阶段。

Tip

判断是否需要停顿,不能只看“存在 RAW”。还要看生产者何时产生数据、消费者何时真正需要数据,以及处理器是否提供对应的转发路径。

(2)Load-use:必须停 1 拍🧀

1
2
3
lw  x5, 0(x1)     # x5 <- M[x1]
add x6, x5, x2    # 立即使用刚加载的 x5
sub x7, x6, x3    # 使用 add 的结果

lw 读取的数据要到第 4 周期的 MEM 结束后才可用,但 add 原本会在第 4 周期进入 EX,数据来得太晚。因此必须让 add 在 ID 多停一拍,同时冻结后面的 sub 和 PC,并向 EX 注入一个气泡。

指令 / 周期 1 2 3 4 5 6 7 8
lw x5, 0(x1) IF ID EX MEM WB — — —
add x6, x5, x2 — IF ID ID(停) EX MEM WB —
sub x7, x6, x3 — — IF IF(停) ID EX MEM WB
注入的气泡 — — — EX(NOP) MEM WB — —

第 5 周期,lw 的加载结果从 MEM/WB 流水寄存器转发给 add 的 EX 输入。随后 add 产生的 x6 又可以转发给 sub,因此第二个 RAW 不再额外停顿。

(3)不相邻的 RAW:通常可由转发解决🧀

1
2
3
add x5, x1, x2
or  x8, x3, x4
sub x6, x5, x7

sub 与 add 之间隔了一条独立指令。sub 在第 5 周期进入 EX 时,add 正在 WB,可以通过写回后的寄存器值或相应转发路径取得 x5。

指令 / 周期 1 2 3 4 5 6 7
add x5, x1, x2 IF ID EX MEM WB — —
or x8, x3, x4 — IF ID EX MEM WB —
sub x6, x5, x7 — — IF ID EX MEM WB

若题目明确说明没有转发,就必须等生产者把结果写回寄存器后,消费者才能完成读寄存器;所需停顿数会比上表更多。


三、冲突控制:气泡法与重定向法🧀

处理 RAW 冲突主要有两种思路:让消费者等数据,或者把数据提前送给消费者。前者是气泡法,后者在本课程中称为重定向法,通常也叫数据转发或旁路。

1、气泡法🧀

气泡法通过暂停部分流水级,让相关指令之间空出足够时间。发生冲突时,控制器执行三件事:

  1. 禁止 PC 写入,下一周期不再取新指令。
  2. 禁止 IF/ID 流水寄存器写入,使消费者继续停在 ID,后续指令继续停在 IF。
  3. 把送入 ID/EX 的控制信号清零,相当于向 EX 注入一条 NOP。
1
2
3
4
生产者 ──继续向后执行──▶
消费者 ──停在 ID────────▶ 等待数据
后续指令 ─停在 IF────────▶
EX 阶段  ──插入 NOP──────▶ 形成气泡

气泡会沿流水线继续流动,并占用周期,但不会改变体系结构状态。

使用场景 需要的处理
没有转发硬件的 RAW 一直停到生产者写回后才能读取
有转发的 load-use 通常停 1 拍,再把加载结果转发给消费者
结构资源被占用 暂停其中一条指令,等待资源空闲

2、重定向法🧀

重定向法不等待结果按正常路径写回寄存器,而是用多路选择器把较后流水级产生的数据重新定向到消费者的输入端:

数据来源 重定向到 典型场景
EX/MEM 中的 ALU 结果 当前 EX 的 ALU 输入 相邻 ALU 指令存在 RAW
MEM/WB 中即将写回的数据 当前 EX 的 ALU 输入 生产者与消费者相隔一条指令
MEM/WB 中的加载结果 停顿后的消费者 EX 输入 load-use 先停 1 拍再转发

简化的转发判断逻辑为:

1
2
3
4
5
6
7
8
9
if EX/MEM.RegWrite
   and EX/MEM.rd != 0
   and EX/MEM.rd == ID/EX.rs:
       从 EX/MEM 转发

else if MEM/WB.RegWrite
        and MEM/WB.rd != 0
        and MEM/WB.rd == ID/EX.rs:
            从 MEM/WB 转发

对第二个源寄存器也要进行同样的判断。如果 EX/MEM 和 MEM/WB 都匹配,应优先选择 EX/MEM,因为它保存的是更新的结果。

重定向不能解决所有 RAW

lw 的数据到 MEM 末尾才产生,而紧随其后的消费者在同一周期开始时就需要进入 EX。数据产生时间晚于使用时间,所以单靠重定向来不及,必须先用气泡法停 1 拍,再进行转发。

3、两种方法的关系🧀

冲突 仅气泡法 重定向法 最终处理
ALU → 下一条 ALU 可以解决,但损失周期 可以直接解决 重定向,不停顿
lw → 下一条使用结果 可以解决 单独使用无法解决 1 个气泡 + 重定向
没有匹配转发路径 可以解决 无法使用 插入足够气泡

“气泡”和“重定向”不是互斥方案。处理器先检查能否及时转发;若数据仍来不及到达,就暂停流水线,等到数据可用后再转发。

与 PC 重定向的区别

数据冲突中的“重定向”是把结果旁路到 ALU 输入;控制冲突中的“PC 重定向”是把取指地址改到分支目标。二者都通过选择新来源改变正常通路,但重定向的对象不同。


四、控制冲突🧀

分支结果确定前,流水线可能已经按顺序取入了后续指令。若最终发生跳转,需要:

  1. 把 PC 重定向到正确目标地址。
  2. 清除已经进入流水线的错误路径指令,即执行 flush。
情况 处理
分支不发生 继续执行顺序取入的指令
分支发生且未预测 重定向 PC,冲刷错误路径
分支预测正确 通常不产生额外罚时
分支预测错误 冲刷预测路径并从正确地址重新取指

把分支后尚未确认的指令简单替换为 NOP 可以保证正确性,但会损失性能。现代处理器通常借助静态或动态分支预测减少控制冲突。

流水线时间计算

解题时按以下顺序处理:

  1. 确定流水段数 \(k\) 和时钟周期。
  2. 先计算无冲突时的 \(k+n-1\) 个周期。
  3. 按数据、结构和控制冲突逐项增加停顿或冲刷周期。
  4. 注意题目是否允许转发、分支在哪一级确定,以及第一条和最后一条指令是否完整进入、退出流水线。

最终总时间为:

\[ \boxed{T_{total}=(k+n-1+N_{stall}+N_{flush})T} \]

其中 \(N_{stall}\) 为全部停顿周期数,\(N_{flush}\) 为控制冲突导致的额外周期数。