CS152 · 第 11 课

Complex Pipelines:从 Scoreboard 到寄存器重命名

存储单元讲完了,课程回到流水线主线,直面一个第 5 课已经埋下伏笔的问题:一旦引入多个延迟不同、可能不流水的功能单元,"简单按顺序发射"的流水线就不够用了。这一课系统建立乱序执行的第一块基石——寄存器重命名。

授课:Sophia Shao 预计用时:60 分钟 难度:较难

为什么复杂流水线难以设计

一旦引入长延迟、部分流水化的浮点单元和延迟可变的访存,经典 5 级流水线的简单假设就会崩塌:执行阶段可能因为某个多周期功能单元未流水化而产生结构冒险;写回阶段可能因为不同功能单元延迟不同而产生结构冒险(多条指令同时想写寄存器堆);延迟不同还会导致乱序写回冒险;异常处理也变得复杂。第 5 课给出的一种解法是"人为拉长所有路径、让写回都对齐到统一的提交点"——但这意味着遇到长延迟操作(如除法、缓存缺失)整条流水线都要停顿。这一课要问:能不能不靠"拉齐延迟+旁路",而是真正允许乱序完成、甚至乱序发射?


数据冒险分类回顾

考虑寄存器-寄存器指令序列 r_k ← r_i op r_j:RAW(写后读)——数据依赖,如 r3←r1 op r2 之后 r5←r3 op r4;WAR(读后写)——反依赖,如 r3←r1 op r2 之后 r1←r4 op r5;WAW(写后写)——输出依赖,如 r3←r1 op r2 之后 r3←r6 op r7。检查这三类冒险需要一张"发射表":Name/Busy/Op/Dest/Src1/Src2——发射时检查功能单元是否空闲(Busy 列)、检查 RAW(在 Dest 列里搜索本指令的源寄存器)、检查 WAR(在 Src 列里搜索本指令的目的寄存器)、检查 WAW(在 Dest 列里搜索本指令的目的寄存器)。


乱序发射:只带来"有限"的改善

如果只是把发射从"严格按顺序"放宽为"乱序发射"(即指令进入一个发射窗口等待,只要 RAW 满足就可以发射,不必等前面还没就绪的指令),会怎样?课程给出一个具体的 6 指令乱序发射调度实例,得出的结论是——"乱序执行并没有带来显著的改善!" 原因是调度中仍然残留一个无法回避的 WAR 式反依赖停顿:只要 ISA 寄存器数量有限,某条较晚的指令想复用一个寄存器名字,就必须等前面所有对该名字的读操作完成,乱序发射本身消除不了这个限制。

核心洞察:流水线里能同时容纳多少条指令,取决于 ISA 寄存器的数量。 这正是寄存器重命名(register renaming)存在的原因——Robert Tomasulo(IBM,1967 年)为了克服 IBM 360 只有 4 个浮点寄存器这一限制,发明了"动态重命名":把同一个体系结构寄存器名字,在不同时刻映射到不同的物理存储位置(tag),从而让 WAR/WAW 这类"假"依赖直接消失。把上面同一个 6 指令例子里的 f4 重命名为 f4' 后,反依赖被彻底消除,乱序调度的效果立刻显著改善。


重命名机制:重排序缓冲区(ROB)

译码阶段负责重命名,并把重命名后的指令放入重排序缓冲区(Reorder Buffer, ROB)——按程序顺序排列的循环缓冲区,每一项包含 Ins#、use(有效位)、exec(已开始执行位)、op、p1/src1、p2/src2 等字段;p(presence,"存在")位标记源操作数字段里存的是真实数据还是仍然只是一个标签(tag)。译码时给每条指令分配一个 ROB 槽位对应的标签 t,标签在指令完成时被释放。重命名之后,ROB 中任何"RAW 已满足"的指令都可以发射——WAR/WAW 已经被重命名彻底消灭,剩下的调度自由度纯粹由真实的数据依赖(RAW)决定,这正是"乱序/数据流执行"的本质。指令的执行阶段分为四步:取指 → 译码/重命名 → 发射(执行)→ 完成 → 提交,每一步之间都有缓冲(取指缓冲、发射缓冲、结果缓冲),直到架构状态。

一个真实历史设计的例子——IBM 360/91 浮点单元(R.M. Tomasulo, 1967):6 个 load buffer、3 个加法保留站、2 个乘法保留站、4 项浮点寄存器堆,全部通过一条公共结果总线广播 <tag, result>;凡是等待这个 tag 的保留站/寄存器都会匹配上并拷贝数值、置位"存在"标志。(旁注:把标签替换成真实数值本身是一项昂贵的操作。)


为什么乱序执行"消失"了几十年,又在 1990 年代重新出现

乱序执行在 1960 年代之后长期"淡出视野",直到 1990 年代才重新流行,原因有二:精确陷阱(precise traps)——不精确的陷阱会大幅增加调试和操作系统代码的复杂度(相比之下精确中断相对容易提供);分支预测——控制冒险严重限制了指令级并行度,如果分支预测不够好,乱序执行带来的收益会被浪费掉。此外,更简单的机器用更新的工艺技术,往往能打败工艺落后但设计复杂的机器(处理器+缓存终于能塞进单颗芯片;微处理器性能一度以约每周 1% 的速度提升)。

解决精确陷阱问题的关键设计是:取指、译码(in-order)→ ROB/执行(out-of-order)→ 提交(in-order)。ROB 把"完成"和"提交"分离:完成的结果先存进 ROB(可以被后续指令通过旁路提前使用),只有到达提交阶段、按程序顺序轮到该指令时,才真正、不可撤销地更新架构状态——这正是第 4 课"提交点"概念在乱序机器上的精确对应。


常见误区


知识检查

问题 1:为什么"乱序发射"本身几乎不带来改善,而"乱序发射 + 寄存器重命名"却能显著改善调度?

问题 2:寄存器重命名机制最初是谁发明的?为了解决什么问题?

问题 3:为了在乱序执行的同时依然保证精确陷阱,流水线的哪个阶段必须严格按程序顺序进行,且只有到达这个阶段才真正、不可撤销地更新架构状态?


本课要点

乱序发射不够

没有重命名时,有限的 ISA 寄存器数量限制了调度自由度

寄存器重命名

Tomasulo(1967),消除 WAR/WAW 假依赖

ROB

按程序顺序持有重命名后的指令,完成与提交分离

精确陷阱是关键

取指/译码顺序、执行乱序、提交顺序——这套三段式设计让乱序执行重新流行


参考资料与延伸阅读