Archipelago

Lec 8分支预测

讲义完成更新于 2026-09-29

Lec 8 分支预测

TL;DR

  • 分支预测旨在消除控制冒险带来的流水线冲刷气泡,是维持深流水线高 IPC 的决定性技术。
  • 分支目标缓冲区(BTB)在 Fetch 阶段提供零延迟跳转目标预测,而分支历史表(BHT)通过 2 位饱和计数器提升方向预测准确率。
  • 返回地址栈(RAS)以专用后进先出硬件栈精准预测过程调用(JAL/JALR)的动态返回地址。
  • 双纪元(Dual-Epoch)机制分层管理 Decode 早期纠错与 Execute 精确纠错,以严格的优先级仲裁保障推测执行的原子一致性。

MIT 6.1920 · Constructive Computer Architecture 讲师:Thomas Bourgeat / Arvind · 日期:2024-03-19

1. 分支指令频率与代价

定义

定义 — 控制流惩罚(Control Flow Penalty)

处理器在 Execute 阶段才能确定分支目标,而 Fetch 阶段每周期都必须提供下一条指令地址。若流水线有 DD 级在 Execute 之前,每次预测失败将白白执行 DD 条错误路径指令,造成 DD 周期惩罚。

统计数据(Blem et al.,SPEC INT/FP 2006):

  • ARM Cortex-A7 整数负载:每 12 条指令中有 1 条分支
  • x86 整数负载:每 10–11 条指令中有 1 条分支
  • 浮点负载:每 8–27 条指令中有 1 条分支

2. RISC-V 分支指令分类

指令方向已知时机目标已知时机
JALDecode(无条件)Decode(PC 相对)
JALRDecode(无条件)Execute(寄存器)
BEQ/BNE/...Execute(条件)Decode(PC 相对)

3. 分支目标缓冲区(Branch Target Buffer, BTB)

定义

定义 — BTB(Branch Target Buffer)

BTB 是一个小型缓存,记录历史上发生过跳转的指令的(PC → 目标 PC)映射。Fetch 阶段查询 BTB,若命中则用预测目标地址替代 PC+4;命中率很高,即使容量很小(因常跳转的指令反复被预测)。

BSV 接口:

interface AddrPred;
    method Addr nap(Addr pc);                               // Next Address Prediction
    method Action update(Addr pc, Addr nextPC, Bool taken); // 训练
endinterface

更新策略:

  • 分支 taken → 用(pc, nextPC)更新 BTB
  • 分支 not-taken → 删除 BTB 中该 pc 的条目
module mkBtb(AddrPred);
    RegFile#(BtbIndex, Addr) ppcArr <- mkRegFileFull;
    RegFile#(BtbIndex, BtbTag) tagArr <- mkRegFileFull;
    Vector#(BtbEntries, Reg#(Bool)) validArr <- replicateM(mkReg(False));
    ...
endmodule

4. 方向预测:分支历史表(Branch History Table, BHT)

定义

定义 — BHT(Branch History Table)

BHT 是一个大型表(4K 项),每项存 2 位饱和计数器(saturating counter),记录该分支历史上更多 taken(≥10)还是 not-taken(≤01)。BHT 在 Decode 阶段使用(指令类型已知),可将预测准确率提高到 80–90%。

2 位饱和计数器状态机:

强不跳(00)↔弱不跳(01)↔弱跳(10)↔强跳(11)\text{强不跳}(00) \leftrightarrow \text{弱不跳}(01) \leftrightarrow \text{弱跳}(10) \leftrightarrow \text{强跳}(11)

  • 连续两次 not-taken 才从"弱跳"改为"不跳"(迟滞性 hysteresis 减少误翻转)

例题 — 1 位 vs . 2 位预测器循环分析

循环分支:99 次 taken,1 次 not-taken,再循环。

  • 1 位预测器:每次循环出错 2 次(退出时预测 taken,进入时预测 not-taken)
  • 2 位预测器:每次循环出错 1 次(仅退出时出错,下次进入循环时计数器已恢复)

Sol:2 位预测器对循环类模式显著优于 1 位。


5. 多预测器流水线集成

4 级流水线带 BTB + BHT 设计:

  • Fetch:查 BTB → ppcF = btb.nap(pc)
  • Decode:查 BHT → 若方向预测与 BTB 不一致,更新 dEp 并重定向
  • Execute:验证并训练 BTB/BHT

双纪元(Dual Epoch)机制:

  • eEp:Execute 重定向计数
  • dEp:Decode 重定向计数
  • Execute 的重定向优先级高于 Decode(Execute 得到的是精确结果)
// Decode 阶段:先检查 eEp,再检查 dEp
if (eEp[1] != ieEp) // Execute 已经重定向了,丢弃
else if (dEp[0] != idEp) // Decode 自身重定向了,丢弃
else // 正常指令,咨询 BHT

6. 返回地址栈(Return Address Stack, RAS)

定义

定义 — RAS(Return Address Stack)

JALR 用于函数返回时,其目标是运行时才确定的调用点地址,BTB 难以预测(同一函数被多处调用)。RAS 是一个硬件小栈:调用函数时 push PC+4,返回(JALR)时 pop 预测目标,准确率极高。


7. 两级分支预测(Two-Level Branch Predictor)

利用历史相关性(spatial correlation):用最近 N 条分支的方向(全局历史寄存器 Global History Register)作为 BHT 索引,比单用 PC 更准确:

BHT 索引=PC[k:0]⊕GHR[N−1:0]\text{BHT 索引} = \text{PC}[k:0] \oplus \text{GHR}[N-1:0]

Pentium Pro 用最近 2 条分支历史,准确率约 95%。

推论 — 分支预测的本质

分支预测本质上是「记住过去」:识别(PC, 上下文历史)组合,这足以预测绝大多数分支。不可预测分支(如密码学中的数据驱动跳转)仍是安全漏洞的来源(Spectre 攻击)。


核心机制小结与推测预测器协同

BTB 在 Fetch 阶段提供目标地址预测;BHT 在 Decode 阶段提供方向预测;双纪元机制保证多预测器的重定向优先级正确;RAS 专门优化函数返回;两级预测器利用历史相关性进一步提升精度。现代处理器通常组合三种以上预测器,总体准确率超过 97%。

核心机制思考与底层洞察

在高性能流水线微架构中,引入分支预测器不仅仅是提升算法命中率,更是一场严苛的微架构并发一致性考验:

  1. 早期重定向与二次推测的风险:若只在 Execute 阶段纠正预测失误,Fetch 与 Decode 之间的流水线气泡依然十分沉重。让 Decode 阶段介入分支纠错(例如发现无条件跳转 JAL 或咨询 BHT)可以尽早将错误路径掐断。然而,这引入了“基于推测的二次推测”——如果 Decode 自身正在执行一条已经被 Execute 判定为无效的废弃分支,它的重定向操作反而会污染 Fetch 级的 PC!
  2. 双纪元的偏序锁步机制:BSV 的双纪元设计通过形式化的偏序规则优雅地锁定了优先级:令 Execute 纪元记为 eEpeEp,Decode 纪元记为 dEpdEp。微架构在硬件规则上强制执行:eEpeEp 的有效性校验严格先于 dEpdEp。Decode 规则在重定向 Fetch 之前,必须首先确认自身没有被 Execute 冲刷(eEp[1]==ieEpeEp[1] == ieEp);如果检测到 Execute 已经递增了 eEpeEp,Decode 会无条件丢弃当前指令,其私有的 dEpdEp 重定向请求被直接熔断。这种自底向上的分层纪元隔离,使得多预测器在多流水级并发重定向成为一种零死锁、数学可证明的确定性硬件结构。 :::