L06:复杂流水线(Complex Pipelining)
MIT 6.5900 Fall 2024 · Daniel Sanchez 主题:复杂流水线动机、CDC 6600、浮点单元与功能单元特性、复杂流水线控制问题、依赖分析、记分牌(Scoreboard)
一、复杂流水线的动机
当我们想在以下情形下追求高性能时,指令流水线变复杂:
- 多周期操作:全/部分流水化的浮点单元、长延迟操作(如除法);
- 变延迟操作:访问时间可变的存储系统;
- 复制的功能单元:多个浮点或访存单元。
二、CDC 6600(Seymour Cray, 1963)
- 60 位字的快速流水机,128K 字主存、32 体;
- 十个功能单元(并行、非流水):浮点(加法器、2 乘法器、除法器)、整数(加法器、2 增量器……);
- 硬连线控制;用记分牌做指令动态调度;十个外围处理器做 I/O;10 MHz(FP 加 4 周期);
- 世界最快机器达 5 年(直到 CDC 7600)。
Load/Store 架构:分设 8 个 60 位数据寄存器(X)、8 个 18 位地址寄存器(A)、8 个 18 位索引寄存器(B);所有算术逻辑指令都是寄存器到寄存器,只有 Load/Store 访存(碰地址寄存器 1–5 触发 load、6–7 触发 store,对向量操作很有用)。
三、浮点 ISA 与功能单元
浮点 ISA(RISC-V F/D 扩展)
浮点与整数数据通路的交互很大程度由 ISA 决定:
- 浮点与整数分设寄存器文件,唯一交互是一组 move 指令;
- FPR 与 GPR 分设 load/store,但都用 GPR 算地址;
- 分支只取 GPR 为源;FP 比较指令把结果写入 GPR,再用于分支。
浮点单元(FPU)
比整数单元硬件多得多。单周期 FPU 是坏主意。常见多个 FPU、不同类型(Fadd / Fmul / Fdiv……),可全/部分/不流水化。要并发运行多个 FPU,寄存器文件需更多读写端口。
功能单元特性
功能单元有内部流水寄存器 ⇒ 指令进入时锁存操作数 ⇒ 长延迟操作期间功能单元的输入(如寄存器文件)可改变。分全流水(每周期接受新指令)与部分流水(隔几周期接受)。
真实存储系统
主存访问延迟通常远超一周期且常不可预测。改进途径:分设指令与数据存储端口(⇒ 无自修改代码)、Cache(命中单周期、缺失停顿)、交错存储(多访问 ⇒ 体冲突)、分相访存(split-phase ⇒ 乱序响应)。
四、复杂流水线结构与控制问题
结构:IF / ID / Issue → {ALU, Mem, Fadd, Fmul, Fdiv} → WB,配 GPR 与 FPR。
复杂流水线控制问题:
- 执行级结构冒险:某 FPU 或访存单元非流水且多周期;
- 写回级结构冒险:不同功能单元延迟不同;
- 乱序写冒险:不同功能单元延迟不同导致;
- 如何处理异常?
复杂的按序流水线
- 延迟写回使所有操作到 W 级延迟相同 → 写端口不会超额订阅(每周期一进一出)。如何防止增大的写回延迟拖慢单周期整数操作?→ 旁路;
- 对长延迟操作(除法、Cache 缺失)停顿流水线;
- 异常?→ 推测异常不发生,在提交点按程序序检测并恢复。
超标量按序流水线
每周期取两条指令,若一条整数/访存、一条浮点则同时发射——以低成本提升吞吐(如 Alpha 21064、MIPS R5000)。可扩展到更宽发射,但寄存器文件端口与旁路成本增长很快(如 4 发射 UltraSPARC)。
五、依赖分析
三类数据冒险
考虑序列 rk ← (ri) op (rj):
- 数据依赖(真依赖):
r3←r1 op r2; r5←r3 op r4→ RAW(写后读); - 反依赖:
r3←r1 op r2; r1←r4 op r5→ WAR(读后写); - 输出依赖:
r3←r1 op r2; r3←r6 op r7→ WAW(写后写)。
检测数据冒险
定义指令
- RAW 若
; - WAR 若
; - WAW 若
。
寄存器 vs 内存依赖
寄存器操作数的数据冒险在译码级即可确定;内存操作数的冒险只有算出有效地址后才能确定((a1)+offset1 == (a4)+offset2?)。本讲只关注寄存器依赖。
乱序完成、按序发射
指令延迟不同(如 FDIV 4、FLD 1、FMUL 3)会导致乱序完成,可能引起:结构冒险、数据冒险、异常问题。
六、记分牌(Scoreboard)
动态检测冒险的硬件数据结构。能否在不均衡所有流水线深度、也不用旁路的情况下解决写冒险?
何时发射安全
停顿发射直到确定不会引起依赖问题。发射前检查:
- 所需功能单元是否可用?
- 输入数据是否可用?(RAW)
- 写目的是否安全?(WAR? WAW?)
- 写回级是否有结构冲突?
用于正确发射的数据结构
跟踪各功能单元状态的表(Name / Busy / Op / Dest / Src1 / Src2),Issue 级查询:
- FU 可用?→ 查 Busy 列;
- RAW?→ 在 Dest 列搜索本指令的源;
- WAR?→ 在 Src 列搜索本指令的目的;
- WAW?→ 在 Dest 列搜索本指令的目的。
无冒险则加表项,写回后移除。
按序发射下的简化
若操作数在发射时被功能单元锁存:
- 已发射指令会引起 WAR 吗?→ 不会(操作数在发射时已读);
- 会引起 WAW 吗?→ 会(乱序完成)。
故:无 WAR ⇒ 无需保留 src1/src2;Issue 级遇 WAW 不发射 ⇒ 一个寄存器名在 Dest 列至多出现一次 ⇒ 可用位向量编码。
按序发射的记分牌(两个位向量)
- Busy[FU#]:指示功能单元可用性(硬连线到 FU);
- WP[reg#]:记录哪些寄存器有挂起的写(Issue 置真,WB 置假)。
发射时检查:FU 可用?→ Busy[FU#];RAW?→ WP[src1] or WP[src2];WAR?→ 不会出现;WAW?→ WP[dest]。
记分牌动态示例(I1–I6 的 FDIV/FLD/FMUL/FSUB/FDIV/FADD),逐周期记录功能单元状态、保留写的寄存器(WP)与写回时刻——体现按序发射、乱序完成。
七、预告:现代乱序超标量核
- L06(本讲):按序发射的复杂流水线;
- L07:乱序执行与寄存器重命名;
- L08:分支预测;
- L09:推测执行与恢复;
- L10:高级访存操作。
核结构:取指 → 指令缓冲 → 译码/重命名 → 分派(按序)→ 保留站 → 各功能单元(乱序)→ 重排序缓冲 → 退休(按序),配分支预测与 D-Cache。
小结
- 多周期/变延迟/复制功能单元使流水线变复杂,带来执行级与写回级结构冒险、乱序写冒险与异常难题;
- 按序复杂流水线靠延迟写回 + 旁路 + 长延迟停顿 + 提交点异常处理;超标量按序发射以低成本提吞吐;
- 依赖分为 RAW/WAR/WAW,用值域/定义域交集检测;
- 记分牌动态检测冒险;按序发射 + 发射时读操作数 ⇒ 无 WAR、WAW 靠不发射避免,可简化为 Busy + WP 两个位向量。
下一讲:乱序执行、寄存器重命名与异常