第 6 章 · 指令流水线

把指令执行拆成若干独立阶段、让不同指令同时处于不同阶段:周期短(多周期优点)+ 吞吐≈1 IPC(单周期理想),代价是三类冒险。

6.1 五级流水线

经典 RISC 五级:

IF → ID → EX → MEM → WB
取指  译码  执行  访存  写回
缩写干什么
取指IFPC 取指令到 IR,PC+1(或 +4)
译码/读寄存器ID译 opcode,读 rs1/rs2,符号扩展立即数
执行EXALU 运算 / 算有效地址
访存MEM仅 load/store 用,读写数据内存
写回WB结果写回寄存器文件
周期:    1     2     3     4     5     6     7     8
指令1   IF    ID    EX    MEM   WB
指令2         IF    ID    EX    MEM   WB
指令3               IF    ID    EX    MEM   WB
指令4                     IF    ID    EX    MEM   WB

稳态每周期完成一条指令(IPC≈1),五条同时在场各占一级。总时间 ≈ (k+n-1) 周期(k 级、n 条),比单周期 k×n 快近 k 倍。

对比:单周期周期长、吞吐 1;多周期周期短但吞吐 1/k;流水线周期短且吞吐≈1。本质是把关键路径切成 k 段、每段配锁存器,牺牲锁存器面积和冒险停顿。

6.2 三类冒险(Hazard)

流水线要求每条指令语义与顺序执行一致,违反即冒险:

结构冒险(structural)

两条指令同时要用同一硬件资源。典型:单存储器机里 IF 取指与 MEM 访存同时访内存。解法:分离指令/数据 Cache、寄存器文件「前半个周期写+后半个周期读」、加多端口。

数据冒险(data)

后一条指令要用前一条还没写回的结果:

类型含义危险
RAW读后写(真依赖)后读旧值,必须处理
WAR写后读(反依赖)顺序执行不出现,乱序才出现
WAW写后写(输出依赖)同上,乱序才出现

顺序流水线只需处理 RAW(WAR/WAW 是乱序执行的课题)。典型:

add r1, r2, r3   ; r1 在 EX 算出,WB 才写回
sub r4, r1, r5   ; ID 阶段就要读 r1 —— 但 r1 还没写回!

控制冒险(control)

分支/jump 改 PC,但取指在分支结果算出前就要决定取哪。解法:分支预测(6.4)。

6.3 转发(Forwarding)与停顿(Stall)

转发(旁路):数据在 ALU 一算出、还没写回寄存器时,直接从 EX/MEM 或 MEM/WB 级间锁存器「偷」出来,绕过寄存器文件直接喂给下一条的 EX——解决大多数 RAW,几乎零代价。

add r1, r2, r3    EX 算出 r1 ──┐
sub r4, r1, r5    EX 需要 r1 ◄─┘ 直接旁路,不经过 WB

停顿(stall / 插入气泡):有些 RAW 转发救不了,最典型是 load-use

ld  r1, 0(r2)    ; r1 要到 MEM 结束才知道
add r3, r1, r4   ; EX 就需要 r1,转发也来不及

须插入一个气泡让 add 等一周期。编译器用指令调度把无关指令塞进 load 与 use 之间填掉气泡。

对比:转发用「绕路」提前送数据、不损吞吐;停顿用「等待」硬扛、损一周期。取舍看「数据最早在哪级可用」:EX 之后可转发,load 的 MEM 才可用就只能停。

6.4 分支预测

核心是「赌一个方向,赌错就冲刷」。

静态预测:编译时定死——总预测不跳(BTFN),或编译器用 BTB 填提示位。

动态预测:运行时用历史:

  • BTB(分支目标缓冲):记住「这条分支上次跳到哪」,命中直接取目标地址。
  • 两位饱和计数器:00/01/10/11 四态,预测对 +1、错 -1,连续错两次才翻转——避免一次偶发跳变来回翻转(循环 100 次只在进/出各错一次)。
        预测不跳           预测跳
           │  错        错  │
      ┌────┴──┐       ┌────┴──┐
  不跳 00 ──▶ 01      11 ◀── 10  跳
      ▲           ◀──正确──▶          ▲
      └──────── 连续错两次才跨越  ───────┘

注意:预测错代价 = 冲刷已进 IF/ID/EX 的错误指令,损失 k 周期(k≈流水线深度)。现代 CPU 用更深的流水线换主频,代价是预测错更贵,故分支预测器成了性能隐形发动机——也是 Spectre 能从预测器侧信道偷数据的原因。

6.5 与后续 / 语言对照

  • 第 7 章 Cache:结构冒险解法(指令/数据 Cache 分离)同时是 Cache 层设计;load 停顿、预测错冲刷都受存储层次延迟支配。
  • 第 8 章:乱序执行才让 WAR/WAW 成真问题,需寄存器重命名消解。
  • C/Java:分支预测对「有序数据的循环」友好(forif 稳定,预测率高);if 条件随机则预测率崩到 50%、性能骤降——同 O(n) 代码快慢差几倍的常见来源。