概述

流水线(Pipeline)是计算机体系结构中最成功的性能优化技术之一。其核心思想是将指令执行分为多个阶段,使多条指令能够并行执行。1

流水线 vs 非流水线

非流水线处理器:每条指令完成后再开始下一条

周期 1    周期 2    周期 3    周期 4    周期 5    周期 6
┌──────┐  ┌──────┐
│取指   │  │译码   │  │执行   │  │访存   │  │写回   │
│ I1   │  │ I1   │  │ I1   │  │ I1   │  │ I1   │
└──────┘  └──────┘  └──────┘  └──────┘  └──────┘
          ┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐
          │取指   │  │译码   │  │执行   │  │访存   │  │写回   │
          │ I2   │  │ I2   │  │ I2   │  │ I2   │  │ I2   │
          └──────┘  └──────┘  └──────┘  └──────┘  └──────┘

流水线处理器:各阶段并行处理不同指令

周期 1    周期 2    周期 3    周期 4    周期 5    周期 6
┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐
│取指   │  │译码   │  │执行   │  │访存   │  │写回   │
│ I1   │  │ I2   │  │ I3   │  │ I4   │  │ I5   │
└──────┘  └──────┘  └──────┘  └──────┘  └──────┘

理想情况下,k 级流水线的加速比接近 k 倍。


经典 RISC 流水线

经典五级流水线:

┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐
│  取指   │───▶│  译码   │───▶│  执行   │───▶│  访存   │───▶│  写回   │
│ (IF)   │    │ (ID)   │    │ (EX)   │    │ (MEM)  │    │ (WB)   │
└─────────┘    └─────────┘    └─────────┘    └─────────┘    └─────────┘
阶段全称功能
IFInstruction Fetch从指令缓存取指令
IDInstruction Decode译码并读取寄存器
EXExecuteALU 执行或地址计算
MEMMemory Access访问数据缓存
WBWrite Back结果写回寄存器

流水线 Hazards

流水线存在三种类型的冒险(Hazards),会降低实际加速比。

1. 结构 Hazards(结构冲突)

原因:硬件资源不足以支持所有指令同时执行

经典案例:访存和取指同时访问内存

解决方案

  • 分离指令缓存和数据缓存(Harvard 架构)
  • 插入流水线停顿

2. 数据 Hazards(数据冲突)

原因:指令依赖于前面指令的结果

类型

类型说明示例
RAW读后写(真数据依赖)add $t0, $t1, $t2; sub $t3, $t0, $t4
WAR写后读(反依赖)add $t0, $t1, $t2; sub $t1, $t3, $t4
WAW写后写(输出依赖)add $t0, $t1, $t2; add $t0, $t3, $t4

解决方案

转发(Forwarding / Bypassing)

将结果直接从 ALU 输出传送到需要的位置,无需等待写回。

                        ┌──────────┐
                        │   ALU    │
                        └────┬─────┘
                             │
                     EX/MEM ─┤
                             │
                        ┌────▼─────┐
                        │  转发总线 │
                        └────┬─────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
        ID/EX ─┘        EX/MEM ─┘        MEM/WB ─┘
        (操作数A)      (操作数B)         (操作数B)

流水线停顿(Stall / Bubble)

当转发无法解决时(如 Load 指令),插入气泡等待数据就绪。

正常流水线:
        Cycle:  1    2    3    4    5    6    7
lw     : [IF] [ID] [EX] [MEM] [WB]
add    :       [IF] [ID] [EX] [MEM] [WB]
sub    :            [IF] [ID] [EX] [MEM] [WB]

带停顿的流水线(Load 使用延迟槽):
lw     : [IF] [ID] [EX] [MEM] [WB]
add    :       [IF] [ID] [STALL] [EX] [MEM] [WB]
sub    :            [IF] [STALL] [ID] [EX] [MEM] [WB]

3. 控制 Hazards(控制冲突)

原因:分支指令改变程序计数器(PC)

分支预测

方法准确率复杂度
总是跳转50%最低
动态一跳转60-70%
两比特饱和计数器85-90%
分支目标缓冲(BTB)90-95%

分支延迟槽

在分支指令后放置不依赖分支结果的指令,无论分支是否执行都会执行。

        ┌─────────┐
        │ 分支指令 │
        └────┬────┘
             │
    ┌────────▼────────┐
    │   延迟槽指令     │  ← 始终执行
    │(不依赖分支结果) │
    └────────┬────────┘
             ▼
    ┌────────────────┐
    │  分支目标/下一条  │
    └────────────────┘

流水线冲突检测与控制

流水线控制逻辑

┌─────────────────────────────────────────────────────────┐
│                   流水线控制单元                         │
├─────────────────────────────────────────────────────────┤
│                                                          │
│  IF/ID  ─────┬──────────────────────┬──────────────┐   │
│              │                      │              │   │
│              ▼                      ▼              ▼   │
│  ID/EX  ───▶│ stall                │ forward      │   │
│              │ flush                │              │   │
│              ▼                      ▼              ▼   │
│  EX/MEM ───▶│                      │ forward      │   │
│              │                      │ flush        │   │
│              ▼                      ▼              ▼   │
│  MEM/WB ───▶│                      │ forward      │   │
│              │                      │              │   │
│              ▼                      ▼              ▼   │
│                                                          │
│  输出信号:stall, flush, forward_enable                  │
└─────────────────────────────────────────────────────────┘

停顿条件检测

// 简化的停顿检测逻辑
if (ID/EX.MemRead && 
    (ID/EX.RegisterRd == IF/ID.RegisterRs || 
     ID/EX.RegisterRd == IF/ID.RegisterRt)) {
    stall = true;  // Load-Use Hazard
}

超标量与超流水线

超标量(Superscalar)

多个流水线并行取指、译码、执行:

┌─────────────────────────────────────────┐
│           超标量处理器(2路)              │
├─────────────────────────────────────────┤
│  ┌─────────┐  ┌─────────┐             │
│  │流水线 1 │  │流水线 2 │   ← 并行执行 │
│  └────┬────┘  └────┬────┘             │
│       │            │                   │
│       ▼            ▼                   │
│  ┌─────────┐  ┌─────────┐             │
│  │ 发射队列 │  │ 寄存器重命名 │         │
│  └─────────┘  └─────────┘             │
│       │            │                   │
│       ▼            ▼                   │
│  ┌─────────────────────────────────┐   │
│  │      按序提交 (In-order Commit)   │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

超流水线(Superpipelining)

将流水线级数划分的更细,提高时钟频率:

普通流水线(5级):        超流水线(10级):
┌─────────┐               ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│  取指   │               │IF1│IF2│ID1│ID2│EX1│...│MEM1│MEM2│WB1│WB2│
└─────────┘               └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘

乱序执行

现代处理器采用乱序执行(Out-of-Order Execution)提高并行度:

┌──────────────────────────────────────────────────┐
│              乱序执行引擎                         │
├──────────────────────────────────────────────────┤
│                                                   │
│   ┌────────┐    ┌──────────┐    ┌──────────┐   │
│   │ 取指/译码 │───▶│ 寄存器重命名 │───▶│ 发射队列  │   │
│   └────────┘    └──────────┘    └────┬─────┘   │
│                                       │          │
│                              ┌────────▼────────┐ │
│                              │  保留站/ROB     │ │
│                              └────────┬────────┘ │
│                                       │          │
│                    ┌──────────────────┼──────────┐│
│                    ▼                  ▼          ▼│
│              ┌──────────┐       ┌──────────┐ │
│              │ ALU 1    │       │ ALU 2    │ │
│              └────┬─────┘       └────┬─────┘ │
│                   │                   │        │
│                   └─────────┬─────────┘        │
│                             ▼                   │
│                      ┌──────────┐               │
│                      │  提交队列 │               │
│                      └──────────┘               │
└──────────────────────────────────────────────────┘

寄存器重命名

消除 WAR/WAW 依赖:

原始序列:                  重命名后:
add r1, r2, r3    ──▶      add r1, r2, r3
add r1, r4, r5    ──▶      add r10, r4, r5   // r1 → r10
add r6, r1, r7    ──▶      add r6, r10, r7   // 使用 r10

参考

Footnotes

  1. 参考《Computer Organization and Design》by Patterson & Hennessy 和《CSAPP》by Bryant & O’Hallaron