深夜十点,一家新开张的川菜馆后厨灯火通明。老主厨张师傅一手翻着账本,一手举着炒锅:“鱼香肉丝等宫保鸡丁出锅!宫保鸡丁等麻婆豆腐的料!”——每道菜他都要亲自核对进度,灶台明明空着,却没人敢点火,因为“该谁先做”只有账本说了算。这家店的翻台率,全靠张师傅一个人扛。 隔壁的 Tomasulo 大厨刚接手了一家菜馆,他立下两条新规矩:第一,每个灶台旁边挂一块小白板(保留站),把“这道菜需要什么料、料在哪个灶台上正炒着”写得清清楚楚;第二,厨房装了一部透明的传菜电梯(公共数据总线),任何一道菜出锅,电梯就亮灯播报“某菜已出锅”,谁需要谁自己伸手端。从此,后厨不再等主厨发号施令,互不相干的菜同时开炒,翻台率翻了一倍。 这台“后厨管理学”,正是计算机体系结构里的 Tomasulo 算法。它让 CPU 能同时调度几十条指令,是现代处理器乱序执行的心脏。
对 AI 从业者而言,理解保留站、CDB 与寄存器重命名的配合,是看懂乱序执行 CPU 微架构的钥匙,也是做系统性能优化、AI 芯片架构设计时不可绕开的基本功。
目录
可视化演示
展示Tomasulo算法的保留站结构、CDB公共数据总线广播、寄存器重命名机制及与记分板方法对比,支持切换四种调度视角。
本节配有交互动效(进入视口后自动加载,约 3KB)。
Tomasulo算法 保留站:Op+V/Q+Busy CDB: 结果广播+仲裁 ARF→PRF 寄存器重命名
后厨困局:数据冒险
想象张师傅的老后厨:一张大菜谱贴在墙上,顾客按顺序点菜,但他给自己定了一条死规矩——只有上一道菜完整出锅,自己才翻开下一张菜谱。这样永远不会出错,但翻台率低得可怜。流水线餐厅则把“洗菜、切菜、炒菜、装盘”分成四个工位,不同菜可以在不同工位同时流动。可是很快工位之间开始互相掣肘:“宫保鸡丁”要用“鱼香肉丝”炒好的鸡肉丁,后面的菜只得干瞪眼;更麻烦的是,两道菜都点名要用同一个炒锅,锅只有一个,谁先谁后全得主厨拍板。
计算机里的指令和菜谱如出一辙。CPU 把指令在流水线上取指、译码、执行、写回,但指令之间存在“数据冒险”:一条指令要读的寄存器,恰好被前面指令写,称为 RAW(先写后读,真依赖);一条指令要读的寄存器会被后面指令写,称为 WAR(反相关);两条指令写同一个寄存器,称为 WAW(输出相关)。WAR 和 WAW 本质上不是数据非等不可,而是“记账冲突”——寄存器这个名字只有一个,大家却想共用。
比如指令 I2 要读 R3,而 R3 是 I1 刚写的:
如果没有调度,I2 必须等到 I1 写完 R3,即使执行单元空闲也只能干等。记分板方法用一个集中账本记录所有状态,每当遇到冒险就让后续指令整体暂停。
张师傅的账本遇到这类“锅还没腾出来”的事只能停灶等,整条流水线冒烟。Tomasulo 大厨却不这么做——他在每个灶台旁竖起了候单架(保留站),让等待变得“松散而有序”,这正是下一节的主角。
- I1 写目标寄存器把 R1 与 R2 的加和写入 R3,这是后一条指令需要的“半成品”。
- I2 读源寄存器I2 要读 R3 参与乘法,R3 此刻还没写回。
- RAW 依赖判定I1 写在前、I2 读在后,形成先写后读的真依赖,I2 必须等待 I1 提交结果。
候单架:保留站结构
Tomasulo 大厨改造后厨的第一步:每个灶台旁挂一块小白板,专属于这个灶台。白板上固定六个栏目:这道菜要做什么(菜名)、两个配料的备料是否已经到手、两个配料正在哪个灶台上做着。任何一道菜进厨房,先找一块空白的白板占住,把“缺什么、等谁”写清楚,然后就可以去忙别的。
这块“白板”在芯片里叫保留站(Reservation Station,RS)。一条指令发射时,并不直接占用执行单元,而是先找空闲保留站住下,执行单元空闲时再从站里取活儿。于是“指令在等操作数”这件事,从“挡在发射口”变成了“在站里安安静静地等”。每个保留站存六个信息:Op 操作码、Vj 与 Vk 两个源操作数的就绪值、Qj 与 Qk 两个等待标记、Busy 占用位。
发射时,源寄存器如果已经算好(寄存器状态表中无人占用),直接把值抄进 Vj/Vk;如果那个寄存器正被某个站“预订”(还没写回),就把那个站号写进 Qj/Qk,V 先空着。
候单架的精妙在于:把“等待数据”从阻塞流水线,变成坐在椅子上慢慢等。主厨再也不必随时翻账本——白板自己会说话。
- 操作码字段记下“这站要干什么”,比如加法还是乘法。
- 就绪值字段源操作数到位即存入 Vj/Vk,后续执行直接取用,不用再翻寄存器堆。
- 等待标记字段源操作数尚缺时,用 Qj/Qk 记下“哪个保留站将产出它”,相当于白板上写“等 3 号灶”。
- 占用位Busy 为真表示该站已有指令入住,发射阶段不能再放新指令。
传菜电梯:CDB 广播
真正让白板“说话”的,是一台透明传菜电梯。老后厨做好的菜要由张师傅一道一道亲手端到各桌,遇上几桌同时要菜,他就只能排队。Tomasulo 的后厨装了一部通透明亮的电梯:任何一层灶台做完菜,电梯瞬间亮起“3 号灶:宫保鸡丁出锅”,整栋楼的候单架同时看到。谁在等这道菜,谁就自己伸手把料取走。
这部电梯就是公共数据总线 CDB(Common Data Bus)。它携带两个信息:结果值 Value 和标签 Tag(通常是产生这个结果的保留站号)。每个周期,完成执行的保留站把结果挂上 CDB,所有保留站和寄存器堆同时监听比较:如果自己的 Q 正好等于这个 Tag,立刻把 Value 存入 V,并把 Q 清空。
这就是“广播式写回”:一个结果只写一次,却同时送达所有需要的站,省去了逐个通知的等待。
CDB 让“通知”从一对一变成一对多:电梯喊一嗓子,所有人都听到了。老主厨端菜跑断腿,新后厨一部电梯全搞定。
- 监听条件等待标记必须与广播标签一致,才说明“我的料来了”。
- 取用结果把广播出来的值写入本站就绪值槽,执行时可直接使用。
- 清除等待该源操作数已就绪,等待标记清空,不再依赖任何人。
菜牌编号:寄存器重命名
新后厨还有一项秘密武器。两位顾客都点了“招牌鱼”,按老规矩,菜单上只能写一道“招牌鱼”,后面那道菜的名字会把前面顶掉,等到前菜出锅,服务员都不知道该把它记在哪一栏。Tomasulo 大厨想了个法子:不在菜单上写菜名,而给每道菜发一个编号——“5 号灶做 A 单招牌鱼,7 号灶做 B 单招牌鱼”。菜名(寄存器)相同不要紧,编号(保留站标签)能分清谁是谁。
这正是寄存器重命名。Tomasulo 里用一张“寄存器状态表”(Register Status)一列搞定:表中记录“这个寄存器的最新结果将由哪个保留站产出”。发射指令 I 时,目标寄存器 Rd 在表里登记为 I 所入住的保留站号;后面的指令如果读到 Rd,就去那个站等结果,而不是直接去寄存器堆取。
因为读方拿到的不是“寄存器名”而是“站号标签”,后写者覆盖寄存器不再影响先写者的读者——WAR 消失;两条写同寄存器的指令各自登记各自的站号,谁先广播谁先被读到——WAW 也不会串线。
菜牌编号让同名的菜互不挡道;寄存器重命名让同名的寄存器各找各的站。从根源上,WAR 与 WAW 这对“纸面冲突”消失了。
- 登记目标把“谁将写 Rd”记在状态表中,相当于给菜贴上灶台编号。
- 读方等待后续指令读 Rd 时,若表项非空,就把等待标记指向对应保留站。
- 写回清理该站广播结果后,若表项仍指向该站则清除,寄存器回到“无主”状态。
三阶段执行:发射-执行-写回
一张菜谱在 Tomasulo 的后厨要经历三站旅程:进门前台接单、贴上编号(发射);配料备齐后灶台开火(执行);菜熟后传菜电梯广播出锅(写回)。听起来是流水线,但注意:接单严格按顾客顺序,开火和出锅却不——哪道菜料齐了哪道先做,鸡蛋熟了的先上桌。
指令同样走三个阶段:Issue(发射):按程序顺序找空闲保留站,登记操作数与等待标记;Execute(执行):一旦 Q 全部为空(操作数就绪),占用执行单元开始计算;Write Result(写回):把结果连同站号挂上 CDB 广播,同时清空保留站供后续指令使用。三个阶段让“顺序发射 + 乱序执行 + 乱序写回”成为可能。
只要保留站有空位,发射就不停。Tomasulo 的并行度来自这句话:有料的做菜,没料的排队,谁也不空占灶台。
老账本只能一道道菜轮着做;新后厨三站交替运转,同一时刻既有菜在等料,也有菜在开火,还有菜在坐电梯——热闹而有序。
- 发射阶段顺序取指令,登记 Q/V 字段,并更新寄存器状态表。
- 执行阶段Q 全空则开火,按 Op 完成运算,运算延迟因操作而异。
- 写回阶段结果挂 CDB 广播,所有等待站同步收料,本保留站释放。
老账本与新候单架:对比记分板
张师傅的账本(记分板)和新后厨的候单架,到底差在哪?张师傅是“中央集权”:所有指令状态集中在一张表上,主厨要亲自仲裁每个周期谁该读谁、谁该写谁,生怕冒险。Tomasulo 大厨是“地方自治”:每站自己看着 Q/V,数据齐了就动手,CDB 一声广播人人自取,主厨几乎不用操心。
记分板(Scoreboard)是更早的动态调度方案:它集中登记指令的状态、功能单元状态和寄存器状态,每个周期中央控制器判断哪条指令可以读操作数、哪个功能单元可以写结果。它能乱序执行,但由于没有重命名,WAR/WAW 一旦出现就要整体停顿。Tomasulo 的保留站天然实现了重命名,“寄存器名”变成了“站号标签”,于是假依赖消失。
代价是保留站占硬件面积、CDB 布线复杂,功耗也更高。
张师傅账本一页纸,但事事要他拍板;候单架满天飞,但人人自律。Tomasulo 用更多硬件换来了更少等待——这笔买卖,现代 CPU 觉得值。
- 记分板的冒险检测写目标寄存器时若与未完成的读操作冲突,需要暂停等待,效率低。
- Tomasulo 的登记写目标被替换成站号标签,读者去找站而不是等寄存器。
- 硬件代价对比集中式小表省硬件,分布式站与总线面积、功耗更大。
从后厨到云端:现代演进
今天的大酒店早就不是一张白板那么简单了。Tomasulo 的后厨迎进了三位新管家:物理食材仓库(物理寄存器堆)、编号速查表(RAT 重命名映射表)、传菜记录本(ROB 重排序缓冲)。候单架变成了真正的物理寄存器,广播电梯依旧在,但出锅顺序还得由记录本按顾客点单顺序确认。
现代乱序处理器继承并改造了 Tomasulo 思想:用统一的物理寄存器文件(PRF)替代保留站里的 V 槽,用 RAT(Register Alias Table)把架构寄存器映射到物理寄存器,执行完成后结果先进重排序缓冲 ROB,再按程序顺序提交。这样既保留动态调度,又保证精确异常与顺序退休。浮点单元和向量单元因为指令延迟差异大、依赖链复杂,尤其需要这种调度。
Tomasulo 从教科书走进了 Intel、AMD、ARM 的每一个乱序核,也出现在 GPU 与 AI 加速器的调度逻辑中。
从一家小面馆到云端算力集群,核心思想没变:等料的等料,开火的开火,出锅的坐电梯广播。只是今天的后厨,规模从四个灶台变成了数万个执行单元的庞然大物。
- 架构到物理映射RAT 把程序员可见的寄存器名字翻译成真正存放数据的物理寄存器编号。
- 重排序提交ROB 项按发射顺序标记完成状态,确保异常处理和中断时能精确回滚。
- 广播仍是核心CDB 演进为多端口结果总线,一次仍可送达多个等待者。
AI 应用场景
现代 CPU 乱序执行引擎的性能调优
阶段:AI 训练/推理基础设施的性能调优
解决的问题:AI 模型训练依赖大规模矩阵运算,主机 CPU 同时要处理数据加载、控制流和向量指令。若没有 Tomasulo 式调度,一条乘法指令等待数据,后面几十条独立指令都得整体干等。保留站 + CDB 让无依赖指令越过长延迟指令先执行,CPU 利用率大幅提升。
具体案例:推荐系统在线推理服务跑在 x86 服务器上,模型里混杂着访存、比较、向量乘加等指令。现代乱序核心内的 RAT、物理寄存器堆与 ROB 正是 Tomasulo 的子孙——编译器发出指令后,硬件自动把相互独立的比较指令提前执行,掩盖 DRAM 访问的几百周期延迟。
深度学习加速器中的指令级并行调度
阶段:AI 芯片前端设计与算子库底层优化
解决的问题:GPU/NPU 的算子内部有大量相互独立的乘加指令,不同算子延迟差异巨大。调度器要像 Tomasulo 那样,为每个计算单元配备“等待数据”的缓冲,数据一到立刻开工,而不是让整条流水线随最长依赖链空转。
具体案例:CUDA 的性能分析中,warp 调度器会在某个 warp 等待全局内存返回时,切换到另一个可执行 warp——这正是“保留站等待 + 乱序切换”思想的宏观版。PyTorch 的 CPU 后端也会依赖乱序执行把标量控制指令与向量计算重叠起来。
处理器模拟器建模与硬件验证
阶段:AI 集群仿真评估
解决的问题:设计新 AI 加速器前,无法直接流片测试,需要用模拟器建模 Tomasulo 调度逻辑,预估保留站数量、CDB 带宽对性能的影响,避免硬件资源浪费。
具体案例:架构师在 gem5 模拟器中配置 O3CPU 的 ROB 深度、物理寄存器个数,跑一遍 Transformer 推理负载,对比不同配置下的 IPC 与功耗,最终决定芯片的寄存器预算和发射宽度。
Python 演示
# -*- coding: utf-8 -*-# 最简 Tomasulo 调度模拟器# 演示保留站 + CDB 广播让无依赖指令乱序完成 def run(): prog = [ ('I0', 'ADD', 'R1', 'R2', 'R3'), # 加法 2 周期 ('I1', 'MUL', 'R4', 'R1', 'R5'), # 乘法 6 周期, 等 I0 ('I2', 'ADD', 'R6', 'R7', 'R8'), # 加法 2 周期, 无依赖 ('I3', 'MUL', 'R9', 'R6', 'R10'), # 乘法 6 周期, 等 I2 ('I4', 'ADD', 'R11', 'R12', 'R13'), # 独立短指令 ] lat = {'ADD': 2, 'MUL': 6} regs = {} for _, _, rd, rs1, rs2 in prog: for r in (rd, rs1, rs2): regs.setdefault(r, 100) # 初值统一 100 reg_stat = {r: None for r in regs} # 寄存器状态表 stations = [] for i in range(5): stations.append({'name': 'RS%d' % i, 'busy': False, 'op': None, 'rd': None, 'vj': 0, 'vk': 0, 'qj': None, 'qk': None, 'remain': 0, 'started': False}) issued = 0 cyc = 0 order = [] print('=== 最简 Tomasulo 模拟 ===') def write_result(st, c): res = st['vj'] + st['vk'] if st['op'] == 'ADD' else st['vj'] * st['vk'] print('周期%2d: CDB 广播 %s=%d' % (c, st['rd'], res)) regs[st['rd']] = res if reg_stat[st['rd']] == st['name']: reg_stat[st['rd']] = None for other in stations: if other is st: continue if other['qj'] == st['name']: other['vj'] = res other['qj'] = None if other['qk'] == st['name']: other['vk'] = res other['qk'] = None order.append(st['rd']) st['busy'] = False while issued < len(prog) or any(s['busy'] for s in stations): cyc += 1 for st in stations: # 1) 写回: CDB 广播 if st['busy'] and st['started'] and st['remain'] == 0: write_result(st, cyc) if issued < len(prog): # 2) 发射: 顺序发射 ins = prog[issued] free = next((s for s in stations if not s['busy']), None) if free is None: print('周期%2d: 保留站已满, 发射停滞' % cyc) else: free.update(busy=True, op=ins[1], rd=ins[2], started=False, remain=0) if reg_stat[ins[3]] is None: free['vj'] = regs[ins[3]] free['qj'] = None else: free['vj'] = 0 free['qj'] = reg_stat[ins[3]] if reg_stat[ins[4]] is None: free['vk'] = regs[ins[4]] free['qk'] = None else: free['vk'] = 0 free['qk'] = reg_stat[ins[4]] reg_stat[ins[2]] = free['name'] print('周期%2d: %s 发射 -> %s, 目标 %s, 等待 %s/%s' % (cyc, ins[0], free['name'], ins[2], free['qj'] or '-', free['qk'] or '-')) issued += 1 for st in stations: # 3) 执行: 数据就绪就开火 if not st['busy']: continue if not st['started']: if st['qj'] is None and st['qk'] is None: st['started'] = True st['remain'] = lat[st['op']] print('周期%2d: %s 开始执行 %s' % (cyc, st['name'], st['rd'])) else: if st['remain'] > 0: st['remain'] -= 1 seq_total = sum(lat[ins[1]] for ins in prog) print('=== 完成顺序: %s ===' % ' -> '.join(order)) print('程序顺序: I0 I1 I2 I3 I4') print('顺序串行需 %d 周期, Tomasulo 乱序用 %d 周期' % (seq_total, cyc)) if __name__ == '__main__': run()
free.update(busy=True, op=ins[1], rd=ins[2], started=False, remain=0)找到空闲保留站后,一次性登记操作码、目标寄存器等字段,相当于在候单架上写菜名。if reg_stat[ins[3]] is None:检查源寄存器是否已被某个站“预订”,为空表示值立即可用。free['qj'] = reg_stat[ins[3]]源操作数暂缺,记下将产出它的保留站号,等待 CDB 匹配。reg_stat[ins[2]] = free['name']寄存器重命名登记:目标寄存器的最新结果由本保留站负责产出。if st['qj'] is None and st['qk'] is None:两个源操作数都已就绪,数据依赖解除,可以开火。st['remain'] = lat[st['op']]开火后按操作类型设定执行延迟倒计时,模拟不同运算的耗时差异。if other['qj'] == st['name']:CDB 广播时,所有保留站同时比对等待标记,命中即收料。order.append(st['rd'])记录写回顺序,用于展示乱序完成结果。
常见误区
误区有了保留站和 CDB,任何冒险都不会让 CPU 等待,指令完全可以自由飞翔。
正解RAW 依然必须等待——后一条指令真的需要前一条算出的值,菜没出锅厨子再快也做不了。Tomasulo 消除的是 WAR/WAW 这种“假依赖”(寄存器名冲突),靠的是重命名。RAW 是“真依赖”,只优化了等待方式:由集中等待改为在保留站中等待,CDB 结果一到就立刻开工,减少空转,但等待本身无法消失。
为何常见:看到“解决数据冒险”的宣传语就以为什么都解决了,误把“消除假冒险”当成“消除真冒险”。
误区保留站里的 Vj/Vk 存了所有寄存器值,所以寄存器堆可有可无。
正解保留站只暂存“已就绪的源操作数”,通常来自寄存器堆初值或 CDB 刚广播的结果;它并不保存所有寄存器内容。寄存器堆仍是最终写回口,CDB 写回时会同时更新保留站和寄存器文件。可以理解为候单架只记“这单需要的料”,而食材仓库(寄存器堆)还得随时补货。
为何常见:Vj/Vk 与寄存器值长得太像,容易误以为保留站是一套寄存器副本。
误区既然流程是“保留站接单到开火到传菜”,我猜指令可以乱序进入保留站。
正解发射永远是程序顺序,乱序发生在执行和写回阶段。保留站满时,后续指令只能在发射口排队等着。真正乱的是“谁先开始炒、谁先出锅”,而不是“谁先进厨房”。现代乱序处理器同样保持按序发射,乱序的是流水线深处的执行与写回。
为何常见:把“乱序执行”误解为“乱序发射”,一字之差,失之千里。
误区CDB 很强大,所有完成的结果瞬间都能送达,多宽都行。
正解真实硬件受限,通常每周期只能广播 1 到 2 个结果(单 CDB 或双 CDB 总线)。如果多个站同时完成,需要仲裁选一个先广播,其他结果排队等下一个周期。这也是现代 CPU 增加 CDB 端口、采用多结果总线的直接原因。教学模拟为了方便演示,常放宽为允许同时广播多个完成结果。
为何常见:正文强调“一次送达所有人传菜电梯很高效”,忽略了“电梯每次能装几道菜”的物理上限。
面试题
Q1. Tomasulo 算法主要解决什么问题?
- A.解决 CPU 缓存缺失
- B.通过动态调度解决数据冒险并提升指令级并行← 正确答案
- C.实现更准确的分支预测
- D.降低 CPU 发热
答案:B。Tomasulo 通过保留站等待数据、CDB 广播结果和寄存器重命名,让 CPU 能乱序执行指令,从而解决数据冒险、提升并行度。它不是用来处理缓存、分支预测或功耗问题的。
Q2. 公共数据总线 CDB 的核心作用是什么?
- A.把指令按顺序提供给 CPU
- B.在每个周期广播已完成的运算结果← 正确答案
- C.存储所有寄存器的副本
- D.代替程序计数器
答案:B。CDB 把完成写回的结果一次广播(Tag + Value),所有等待该标签的保留站同时收料。这就像透明传菜电梯喊一嗓子,各层需要的人自助取菜。
Q3. 保留站中 Qj 字段表示什么?
- A.源操作数已经就绪
- B.产生该源操作数的保留站编号← 正确答案
- C.目标寄存器编号
- D.指令执行剩余周期
答案:B。Qj 非空表示源操作数 1 正在等待某个保留站的结果,记录的是“谁将产出它”。这正是候单架上“这个料在 6 号灶上正做着”的编号写法。
Q4. Tomasulo 算法在发射阶段什么情况下会停滞?
- A.所有保留站都被占用← 正确答案
- B.CDB 总线正忙
- C.目标寄存器被占用
- D.执行单元温度过高
答案:A。发射需要空闲保留站来“接单”,站满则后续指令按序等待。CDB 忙不影响发射,目标寄存器被占用由寄存器状态表处理而不是阻塞发射。
Q5. 现代 CPU 中 Tomasulo 思想被主要继承为?
- A.缓存预取优化
- B.乱序执行与寄存器重命名← 正确答案
- C.分支预测器设计
- D.中断控制器实现
答案:B。现代乱序处理器将保留站思想演进为物理寄存器堆 + RAT 重命名表 + ROB 重排序缓冲,本质仍是“重命名加乱序调度”这一套 Tomasulo 核心。
Q6. 某条指令保留站中 Qj=RS2 且 Vj 为空,它何时能开始执行?
- A.发射后立即执行
- B.当 RS2 的结果通过 CDB 广播时← 正确答案
- C.等所有先发射的指令都完成
- D.由操作系统手动唤醒
答案:B。Qj 匹配 CDB 广播标签后,值写入 Vj 同时清空 Qj,数据就绪才开火。类似电梯里喊“宫保鸡丁出锅”,等这道菜的候单架立刻伸手取料。
Q7. WAR(写后读)冒险的另一个称呼是?
- A.结构冒险
- B.反相关← 正确答案
- C.控制冒险
- D.访存冒险
答案:B。WAR 称为反相关(Anti-dependence)。虽然前一条读、后一条写,本无真数据依赖,但寄存器名字复用造成了错觉依赖,Tomasulo 用重命名消除它。
Q8. Tomasulo 通过哪些机制消除 WAR 和 WAW 冒险?
- A.保留站占位完成重命名← 正确答案
- B.寄存器状态表登记目标映射← 正确答案
- C.CDB 广播结果值
- D.缓存行预取
答案:AB。保留站以站号标签隔离写目标,寄存器状态表记录“目标寄存器的最新结果将由谁产出”,读者去找对应站,WAR/WAW 随之消失。CDB 只负责传数据,缓存预取与冒险无关。
Q9. 相比记分板,Tomasulo 算法的改进体现在哪些方面?
- A.支持乱序发射指令
- B.动态寄存器重命名← 正确答案
- C.分布式保留站控制← 正确答案
- D.完全不再判断数据冒险
答案:BC。记分板集中控制、不支持重命名,遇到 WAR/WAW 会整体停顿;Tomasulo 用分布式保留站加寄存器状态表实现重命名,消除了假冒险。但两者都按序发射,且仍需识别 RAW 依赖,故 A、D 错误。
Q10. 分析程序:I0: ADD R1,R2,R3;I1: MUL R4,R1,R5;I2: ADD R6,R7,R8。在 Tomasulo 调度下,下列哪些说法正确?
- A.I1 发射时若 R1 正被 I0 写入,则 Qj 记为 RS0← 正确答案
- B.I2 可能比 I1 更早完成写回← 正确答案
- C.I0 写回时 R1 的值通过 CDB 广播← 正确答案
- D.I1 必须等 I2 完成后才能发射
答案:ABC。A 描述寄存器状态表的登记规则;I2 无依赖可先执行,完成早于 I1,体现乱序;I0 写回走 CDB 广播。发射按程序顺序,I1 在 I2 之前发射,不需要等 I2,故 D 错误。