计算机体系结构 12:重命名与乱序执行窗口
重命名与乱序执行窗口
一条长延迟指令尚未完成时,后面的独立指令能否先执行?第 09–11 篇的顺序流水线通过前递、停顿和分支恢复保持正确,但遇到队首等待时,后面的就绪操作仍可能受阻。乱序执行把“按程序顺序进入机器”和“按操作数就绪情况开始计算”分开。寄存器重命名进一步区分不同指令产生的同名寄存器值,使多个版本可以同时存在。
本篇的核心问题是:哪些依赖必须等待,哪些只是寄存器名字重复造成的限制;消除后者以后,加宽发射是否就能缩短执行时间?核心案例只有六条算术操作,支持逐项手算和 Python 事件模型复跑。证据等级为教学时序模型,关键路径另有手算核对。它没有取指、缓存、分支、提交或操作系统,因此六周期的结果不能称为一台完整 CPU 的运行时间。
实验目录为 examples/computer-architecture/ooo/。完整轨迹见 rename.json.txt,独立可运行附件见 rename_model.py.txt。模型的算术语法是题设,不是新增的 RISC-V 指令。
同名寄存器可以表示不同版本的值
设初始架构寄存器满足 ri=i。程序按表中 A 到 F 的顺序执行;latency 是开始执行到结果可用之间的教学周期数。
| 操作 | 顺序语义 | latency | 顺序执行得到的值 |
|---|---|---|---|
| A | r1 = r2 + 8 | 4 | 10 |
| B | r3 = r1 + r4 | 1 | 14 |
| C | r1 = r5 + 15 | 1 | 20 |
| D | r4 = r6 + 24 | 1 | 30 |
| E | r7 = r1 + r4 | 1 | 50 |
| F | r2 = r3 + r7 | 1 | 64 |
B 中的 r1 应当是 A 写出的 10,E 中的 r1 应当是 C 写出的 20。r4 也有相同情况:B 读取初值 4,E 读取 D 写出的 30。寄存器名字没有附带版本信息,执行器必须根据程序顺序确定每个源应读取哪一次写入。
如果只保留一份 r1,并且允许 C 先写入 20,B 随后从这份存储位置取数,就可能算成 24。错误来自 B 读到了错误版本,不能通过“最终 r1 恰好还是 20”发现。逐条结果和操作数的来源都需要核对。
RAW、WAR 和 WAW 分别约束什么
RAW(Read After Write,写后读)是生产者到消费者的真数据依赖。B 需要 A 的结果,A→B 必须保留;B→F、C→E、D→E、E→F 也一样。改变寄存器名字不会提前产生一个尚未算出的值。
WAR(Write After Read,读后写)要求较老的读取得正确旧值。例如 B 读 r1,C 写 r1;若 B 尚未取数,C 不能覆盖 B 所需版本。B 读 r4 与 D 写 r4 也形成这种关系。A 读取 r2 与 F 写入 r2 则是另一个名称复用。WAW(Write After Write,写后写)涉及两次写同一名字,例如 A 和 C 都写 r1,最终架构 r1 必须对应较年轻的 C。
BOOM 的重命名文档把 WAR/WAW 列为重命名能够解除的名称依赖,把 RAW 保留为真正的数据依赖。这一分类说明需要保存什么语义,不规定所有处理器必须采用相同的寄存器文件组织。
这几种关系可以同时出现在一个程序里。A→C 有 WAW,而 A→B→C 的名字使用又引入 RAW 与 WAR。检查依赖时应分别写出源、目标和程序先后,不宜只给每条指令贴一个“有冒险”的标签。
先捕获源映射,再分配新目标
物理寄存器为同一个架构名字提供多个存储位置。初始映射是 ri→pi,A 起依次分配 p8、p9,直到 p13。每条指令重命名时先读取源映射,再替换目标映射:
1 | |
按程序顺序应用以后,得到:
| 操作 | 物理源 | 新物理目标 | 被替换的旧目标映射 |
|---|---|---|---|
| A | p2 | p8 | p1 |
| B | p8、p4 | p9 | p3 |
| C | p5 | p10 | p8 |
| D | p6 | p11 | p4 |
| E | p10、p11 | p12 | p7 |
| F | p9、p12 | p13 | p2 |
B 已经捕获 p8 和 p4;C、D 改变后续指令看到的映射,不会回头修改 B 的源。于是 C 可以在 B 尚未执行时产生 p10=20,D 可以产生 p11=30,而 B 仍从 p8、p4 得到 10 与 4。名称冲突解除后,数值依赖图只剩下:
1 | |
箭头旁的数字在这里表示左侧操作的延迟;更直接的关键路径写法是 A(4) + B(1) + F(1)。图中的 C、D 与 A 可以重叠,但 E 仍须等 C、D 的结果。
同一条指令读写同一个逻辑寄存器时,先读源映射尤其必要。例如 r1=r1+1 必须捕获旧 r1;先更新目标映射会让该指令等待自己的新目标,形成错误的自依赖。这个规则同样适用于一周期重命名多条指令时的内部传递:后面的指令要看见前面已经产生的新映射。
就绪表与空闲表不是同一个问题
映射表回答“这个源引用哪个物理版本”。就绪表回答“那个版本的数值是否已经可用”。空闲表回答“还有哪个物理位置可以分配”。三个问题独立:找到物理源不表示该源已经就绪,找到空闲目标也不表示这条指令能够发射。
本模型只实现前两个问题所需的有限状态。六条操作在 t=0 前全部完成重命名,每个写目标使用一个从未重复分配的位置;它不模拟空闲表耗尽和寄存器回收。BOOM 文档给出的实现会在相应指令提交后回收其 stale destination。实际实现必须结合提交、分支恢复和仍在使用该版本的指令判断回收安全性,不能在映射表被年轻写替换时立即释放旧位置。
以 C 为例,C 分配 p10 时替换了映射中的 p8,但 B 仍引用 p8。若立刻把 p8 交给其他指令覆盖,名称依赖又会变成数据破坏。这个反例说明:逻辑名字不再指向某个位置,与所有消费者都不再需要该位置,是两个条件。
有限窗口的发射规则
发射(issue)是把已就绪操作交给执行单元。本模型每个时刻先使到期结果可用,再从程序顺序最老的就绪操作中选择,最多选择 width 条。未就绪的 B 不妨碍选择更年轻但独立的 C。结果在 issue_time + latency 可用。
题设还固定了六条窗口容量、足够的功能单元、不限制写回端口;t=0 前装入全部操作。这里改变的唯一资源参数是发射宽度。下表每个单元格均为“发射→完成”时刻,来自实际运行输出:
| 操作 | width=1 | width=2 | width=4 |
|---|---|---|---|
| A | 0→4 | 0→4 | 0→4 |
| B | 4→5 | 4→5 | 4→5 |
| C | 1→2 | 0→1 | 0→1 |
| D | 2→3 | 1→2 | 0→1 |
| E | 3→4 | 2→3 | 1→2 |
| F | 5→6 | 5→6 | 5→6 |
width=4 时 C、D 更早结束,E 从 t=3 提前到 t=1 发射,但 F 的另一个源来自 B,仍要等到 t=5。三种配置最后都在 t=6 完成。这与关键路径的六周期下界一致。
为了区分“这个输入不受宽度限制”和“宽度没有作用”,回归另含四条相互独立、延迟都为一的操作。它们在 width=1/2/4 时分别耗时 4/2/1。改变工作负载以后限制因素变了,不能把一个依赖链案例推广为所有程序的性能结论。
窗口、宽度和关键路径怎样分别检查
对于给定依赖图,关键路径约束最早完成时间;宽度限制每个时刻能开始多少操作;窗口大小限制调度器能看见多少候选。增加窗口只有在新纳入的操作提供了有用独立工作时才有收益。本篇窗口恰好装下全部六条,因此没有测量窗口增大的效果。
若真正实现有限分配带宽、少量功能单元或有限写回端口,表中的最早时间可能达不到。那些额外等待要在事件记录中有对应原因,不能靠修改最后的周期数来“模拟”。同样,当前六周期不含顺序提交。F 完成以后架构状态何时正式可见,留给下一篇 ROB 模型处理。
| 观察到的限制 | 需要检查的状态 | 对应问题 |
|---|---|---|
| 源结果未产生 | 生产者及 ready time | RAW 关键路径 |
| 源齐备但没有发射 | 宽度、单元、端口 | 资源限制 |
| 独立操作尚未进入窗口 | 队列容量与分配进度 | 可见候选不足 |
| 同名寄存器互相覆盖 | 源物理版本、旧目标寿命 | 重命名或回收错误 |
复跑与验收
在仓库根目录执行:
1 | |
只复跑本篇附件也可以:
1 | |
累计脚本同时检查后续章节的相关模型;本篇关注 outputs/rename.json。验收应核对 B 的物理源为 p8、p4,E 的物理源为 p10、p11,三种宽度最终 r2 都为 64,依赖案例完成时刻都为 6,独立案例分别为 4、2、1。仅看脚本正常退出不足以解释周期表,应从 A、B、F 三项重新计算关键路径。
这些记录证明指定有限模型的执行符合题设。没有 gem5、RTL 或真机计数器参与,不提供商品 CPU 的吞吐预测,也不证明物理寄存器回收、分支恢复或整套 ISA 正确。
两道练习
练习一: 如果 C 在 t=1 已经完成,但 B 到 t=4 才发射,B 应读到 r1 的哪个值?如果实现是在发射时读取当前逻辑映射,会有什么错误?
解答:B 应使用重命名时捕获的 p8,即 A 产生的 10,加上旧 p4=4 得到 14。发射时重新查当前 r1 映射会读到 C 的 p10=20,得到 24;F 可能因此变成 74。需要修正的是操作数版本捕获,而不是给 B 加一个随意的延迟。
练习二: 把 A 的延迟从 4 改成 2,width=4 的完成时间下界是多少?是否可以直接声称真实处理器提速 1.5 倍?
解答:A→B→F 的长度变成 2+1+1=4;C/D→E→F 为 3,在本模型足够资源的前提下可以四周期完成。六除以四得到 1.5,只描述同一教学模型这个输入的周期比。实际实现还受前端、频率、提交和其他资源限制,当前证据无法给出真机加速比。
参考资料
- BOOM:The Rename Stage。2026-09-19 核查在线版;Purpose of Renaming、Rename Map Table、Busy Table、Free List、Stale Destination Specifiers 分别支持依赖分类、映射、就绪和回收机制。本文有限调度器不声称复刻 BOOM。
- BOOM:The Reorder Buffer。Commit Stage 说明旧物理目标的回收与提交联系,作为下一篇的接口。
- 第 07 篇:有限 RV32I 参考执行器。本篇的有限算术轨迹与 RV32I 累计执行器分开;它没有扩充该执行器的支持清单。






