L07:复杂流水线——乱序执行、寄存器重命名与异常(Out-of-Order Execution, Register Renaming, and Exceptions)
MIT 6.5900 Fall 2024 · Joel Emer 主题:记分牌回顾、按序发射的限制、乱序发射、Little's Law 与寄存器数、Tomasulo 重命名、重排序缓冲(ROB)、精确异常
一、CDC 6600 式记分牌回顾
指令按序发射,仅当不会引起 RAW、不会引起 WAW 时才发射;保证执行阶段至多一条指令能写某寄存器;因按序发射 + 立即读操作数,WAR 不可能发生。两个位向量:Busy[FU#](FU 可用性)、WP[reg#](寄存器是否有挂起写)。
二、按序发射的限制
例(含一条 long 延迟的 FLD):
1 FLD f2, 34(x12) 延迟 1
2 FLD f4, 45(x13) 延迟 long
3 FMUL.D f6, f4, f2 延迟 3
4 FSUB.D f8, f2, f2 延迟 1
5 FDIV.D f4, f2, f8 延迟 4
6 FADD.D f10, f6, f4 延迟 1按序限制使指令 4 无法被分派——尽管它与卡住的指令 2、3 无依赖。
三、乱序发射(Out-of-Order Issue)
- 发射级缓冲保存多条等待发射的指令;
- 译码把下一条指令加入缓冲(若有空间且不引起 WAR/WAW 冒险);
- 可发射缓冲中任何 RAW 已满足的指令(暂设每周期至多一次分派);写回可能使更多指令就绪。
仅靠乱序发射并未带来显著提升——因为 WAR/WAW 冒险仍阻止指令 5 被分派。
四、Little's Law 与寄存器数
在飞指令数限制吞吐,而 ISA 的哪个特性限制了在飞指令数?→ 寄存器数。
例:4 个浮点寄存器、每浮点操作 8 周期 → 只有 ½ 次发射/周期!浮点流水线常因寄存器少而填不满(IBM 360 只有 4 个 FPR)。
关键问题:微架构能否在不损失 ISA 兼容性的前提下用比 ISA 指定更多的寄存器?→ 能。Tomasulo(IBM, 1967)提出基于即时寄存器重命名的巧妙方案。
五、寄存器重命名(Register Renaming)
为每个寄存器写提供一个新位置,从而消除 WAR 与 WAW 冒险(重命名 ⇒ 额外存储)。
in-order: 1 (2,1) . . . . . . 2 3 4 4 3 5 . . . 5 6 6
out-of-order: 1 (2,1) 4 4 5 . . . (2,5) 3 . . 3 6 6 # f4 重命名为 f4'处理寄存器依赖
- 译码做寄存器重命名,为每个寄存器写提供新位置 → 消除 WAR/WAW;
- 重命名后的指令加入发射级结构——重排序缓冲(ROB);ROB 中任何 RAW 已满足的指令可被分派;
- 乱序/数据流执行处理 RAW。
重排序缓冲(Reorder Buffer, ROB)[Smith and Pleszkun, 1985]
ROB 每项:Ins# | use | exec | op | p1 src1 | p2 src2。指令槽成为执行候选当:持有有效指令(use 置位)、尚未开始执行(exec 清零)、两操作数都可用(present 位 p1、p2 置位)。
体系结构寄存器值在哪里并不明显——可能在寄存器文件,也可能在 ROB 某项中(以 tag
或数据 形式)。
重命名与乱序发射
- 源中的名字(tag)何时被数据替换?→ 每当某功能单元产生数据时;
- 名字(tag)何时可复用?→ 每当一条指令完成时;
- 重命名表 + 寄存器文件每项持有数据
或 tag (带 present 位)。
数据驱动执行(Data-Driven Execution)
- 译码级分配指令模板(即 tag
)并把 tag 存入寄存器文件; - 指令完成时其 tag 被释放;
- 用公共总线把
<t, result>立即广播给所有等待该数据的指令(IBM 360/91 Tomasulo 浮点单元的做法,把 tag 替换为值是昂贵操作)。
ROB 循环管理:开始执行置 exec 位;完成时 use 位标记为空闲;仅当 use 空闲才推进 ptr2。
六、Tomasulo 的有效性
重命名与乱序执行 1969 年首次在 IBM 360/91 实现,但只对很小一类问题有效,直到 90 年代中期才重新出现。为什么?
- 没有解决内存延迟问题(它比 FU 延迟问题大得多);
- 使异常变得不精确;
- 还需解决分支/跳转代价问题。
七、精确异常(Precise Exceptions)
异常可视为在两条指令间隐式插入的条件子程序调用。须表现为异常恰好发生在两条指令(
及之前所有指令的效果已完成; 之后任何指令的效果尚未发生; - 处理程序要么中止程序、要么从
重启。
乱序完成对异常的影响:高速下难实现精确异常——想在更早指令的异常检查完成前就开始执行较晚指令。
处理异常的选项
异常造成对下一个 PC 值的依赖。处理选项:停顿、旁路、找别的事做、改架构、推测(最常见)。
对推测的指令,把状态更新延迟到提交;较早的异常须覆盖较晚的。
八、按序提交实现精确异常
指令执行的四个阶段:
- 取指(Fetch):从 Cache 取指令位 —— 按序;
- 译码(Decode):放入相应发射缓冲 —— 按序;
- 执行(Execute):指令与操作数送执行单元,完成时结果与异常标志可用 —— 乱序;
- 提交(Commit / graduation):指令不可逆地更新体系结构状态 —— 按序。
实现:取指与译码按序进入 ROB;执行乱序(乱序完成);提交(写回体系结构状态即寄存器文件与内存)按序。提交前需临时存储保存结果(影子寄存器与存储缓冲)。
精确异常的扩展
ROB 项增加 <pd, dest, data, cause> 字段;按程序序提交到寄存器文件与内存(缓冲可循环维护);异常时通过 ptr1 = ptr2 清空 ROB(store 须等提交才更新内存)。
回滚与重命名
寄存器文件不再含重命名 tag(只持已提交状态),译码级如何找源寄存器的 tag?→ 搜索 ROB 的 dest 字段。
重命名表(Renaming Table)
重命名表是加速寄存器名查找的缓存(每项 tag + valid)。每次取异常后需清空;控制转移时也清有效位。
物理寄存器文件(Physical Register File)
ROB 空间低效——一个数据值可能存于 ROB 多处。
思路:把所有数据值放在物理寄存器文件,tag 既是数据值的名字也是持有它的物理寄存器的名字,ROB 只存 tag。于是 64 位数据值可被 8 位 tag 替换(256 项物理寄存器文件)。
九、分支代价(Branch Penalty)
从取指到分支解析之间,误预测时需 kill 多少指令?现代处理器在 nextPC 计算与分支解析之间可能有 >10 个流水级!
小结
- 按序发射受 WAR/WAW 与寄存器数限制;乱序发射仅靠自身提升有限,寄存器重命名(Tomasulo)消除 WAR/WAW 才是关键;
- Little's Law 说明寄存器数限制在飞指令数从而限制吞吐;重命名提供额外存储以容纳更多在飞指令;
- ROB 支持按序取指/译码、乱序执行、按序提交,从而在乱序核中实现精确异常;物理寄存器文件方案让 ROB 只存 tag、更省空间;
- Tomasulo 早期受限于内存延迟、不精确异常与分支代价——后两者由 ROB 与分支预测解决。
下一讲:分支预测与推测执行