4.15 加载使用冒险
定义
加载使用冒险(Load-Use Hazard)是指一条加载指令(mrmovq、popq)的结果被紧随其后的指令立即使用,而后一条指令在译码阶段就需要该结果。这是转发无法完全解决的场景。
为什么转发无法解决
加载指令的执行流程:
$mrmovq (%rdi), %rax →$取指 $→$译码 $→$执行(地址计算$) →$访存(读内存$) →$写回
↑
结果在这里才可用
$addq %rax, %rbx →$取指 $→$译码(需要$%rax) → ...$
↑
这里需要结果,但内存还没读完
关键问题:加载指令的结果在访存阶段末尾才从内存读取到 valM,而需要该结果的指令在译码阶段就需要它——一个时钟周期内,从访存阶段的输出无法及时转发到译码阶段的输入。
解决方案:暂停 + 转发
Y86-64 流水线处理加载使用冒险的策略是暂停一个周期 + 转发:
- 暂停:检测到加载使用冒险后,将加载指令后的那条指令暂停一个周期(将其冻结在译码阶段)
- 转发:暂停的一个周期里,加载指令完成了访存阶段,valM 可用;在下一个周期通过转发路径将 valM 传递给需要它的指令
周期1: [取指 mrmovq] [译码] [执行] [访存] [写回]
周期2: [暂停] [取指] [译码] [执行] [访存] (本条指令被冻结)
周期3: [下一指令] [取指] [译码] [执行] [访存]
↑ 转发 valM
检测条件
加载使用冒险的检测条件:
(mrmovq或 popq在译码阶段 $→$流水线状态为 S)
&& (S_dstM == d_srcA 或 S_dstM == d_srcB)
&& (d_srcA 或 d_srcB != 0xF)
当条件满足时,流水线控制逻辑插入一个气泡并冻结译码阶段和取指阶段的寄存器。
性能影响
每个加载使用冒险导致流水线丧失一个周期的吞吐量。编译器优化中,通过重新排列指令顺序可以在一定程度上避免加载使用冒险(在加载指令和其他使用该加载结果的指令之间插入无关指令)。