前端性能模型:从 MPKI 到 IPC
前端设计不能从“再加一张表会更准”开始,而要先问:当前 workload 每千条指令损失多少周期,损失来自预测、Cache、TLB 还是后端背压?
最小吞吐模型
先选定统计窗口:每提交 1000 条指令(1 kinst)。设分支误预测、ICache miss 和 ITLB miss 的事件率分别为 M_b、M_i、M_t,其平均有效代价分别为 P_b、P_i、P_t。粗略前端损失为:
lost_cycles_per_kinst ≈ M_b·P_b + M_i·P_i + M_t·P_t + starvation_cycles
effective_IPC ≈ 1000 / (1000/base_IPC + lost_cycles_per_kinst)
符号和单位
| 符号 | 含义 | 单位 | 例子 |
|---|---|---|---|
W | 每周期最多可提交的指令数,即机器提交宽度上限 | instruction/cycle | CommitWidth=8 时 W=8 |
base_IPC | 暂时移除所统计前端损失后,机器在该 workload 上可达到的 IPC;它不是必然等于 W | instruction/cycle | 后端仍有限制时可能为 4.0 |
M_b | 每 1000 条已提交指令发生的分支误预测次数,即 branch MPKI | event/kinst | M_b=3.5 |
P_b | 每次分支误预测造成的平均不可隐藏停顿 | cycle/event | 平均损失 16 周期 |
M_i | 每 1000 条已提交指令的 ICache miss 次数 | event/kinst | M_i=1.2 |
P_i | 每次 ICache miss 造成的平均不可隐藏停顿 | cycle/event | 平均损失 22 周期 |
M_t | 每 1000 条已提交指令的 ITLB miss 次数 | event/kinst | M_t=0.15 |
P_t | 每次 ITLB miss 造成的平均不可隐藏停顿 | cycle/event | 平均损失 35 周期 |
starvation_cycles | 未被前三类事件解释、但前端无法向后端提供有效指令的周期数 | cycle/kinst | 跨行、FTQ full 等共 12 周期 |
lost_cycles_per_kinst | 每 1000 条指令由上述前端事件增加的总周期 | cycle/kinst | 计算结果 99.65 |
effective_IPC | 加入这些前端损失后的估算 IPC | instruction/cycle | 计算结果 2.86 |
这里 M_i·P_i 是乘法,不是 M_i/P_i 的除法。量纲检查如下:
(event / kinst) × (cycle / event) = cycle / kinst
而基础执行时间是:
1000 instruction ÷ base_IPC(instruction/cycle)
= 1000/base_IPC cycle
两者都是“执行 1000 条指令所需的周期”,所以可以相加。
完整计算例子
假设测得:
base_IPC = 4.0
M_b = 3.5, P_b = 16
M_i = 1.2, P_i = 22
M_t = 0.15, P_t = 35
starvation_cycles = 12
各项损失为:
branch loss = 3.5 × 16 = 56.00 cycle/kinst
ICache loss = 1.2 × 22 = 26.40 cycle/kinst
ITLB loss = 0.15 × 35 = 5.25 cycle/kinst
other loss = 12.00 cycle/kinst
lost_cycles_per_kinst = 99.65 cycle/kinst
base_cycles_per_kinst = 1000 / 4.0 = 250 cycle/kinst
total_cycles_per_kinst ≈ 250 + 99.65 = 349.65 cycle/kinst
effective_IPC ≈ 1000 / 349.65 = 2.86 instruction/cycle
W=8 只给出绝对上限。这里 base_IPC=4.0,说明即使没有所列前端损失,数据依赖、执行端口、访存和提交利用率仍使程序达不到 8 IPC。
“平均损失周期”应如何理解
P_b/P_i/P_t 不能直接填物理器件的原始延迟。例如一次 ICache miss 从请求到 refill 用了 40 周期,但乱序窗口可能掩盖其中一部分;若最终只让提交额外停了 22 周期,则模型应使用约 22,而不是 40。
有两种常用获取方式:
- 用 top-down 计数器统计归属于该原因的停顿周期,再除以事件数;
- 做受控实验,只改变一种机制,用总周期差除以事件数。
第二种更接近边际代价,但需要确保两次运行路径和指令数可比。
这个模型什么时候会算错
它假设各类损失可以近似相加。真实处理器中存在重叠:一次 branch mispredict 可能发生在 ICache miss 期间,此时把两个完整 penalty 都相加会重复计算。更完整的写法是:
total_cycles = base_cycles
+ branch_loss
+ icache_loss
+ itlb_loss
+ other_frontend_loss
- overlapped_loss
如果使用的 top-down 统计已经保证每个周期只归入一个原因,就不要再次减 overlap;如果各计数器独立累计,则必须检查交集。这个模型的用途是建立优化优先级和数量级判断,不是替代周期精确仿真。
如果误预测损失占主导,扩大 ICache 不会解决核心问题;反过来,如果大部分 branch penalty 与 ICache miss 重叠,单看 M_b·P_b 又会高估预测器优化收益。
分支预测的两个维度
准确率
通常用 MPKI(mispredictions per kilo instructions)比单纯 accuracy 更有意义,因为不同程序分支密度不同。
恢复代价
相同 MPKI 下,从预测到分支解析越深,错误路径占用的周期和资源越多。恢复代价至少包括:发现延迟、redirect 传输、BPU 重新预测、ICache/IFU 重新供给,以及后端重新填充。
有效取指带宽
FetchWidth 只是物理上限。有效带宽还受这些因素限制:
- 取指块内第一个 taken branch 截断后续槽位;
- 32/16 位指令边界和跨行拼接;
- ICache bank、TLB、PMP 与 refill;
- FTQ/IBuffer 满导致的 backpressure;
- 预测覆盖(override)造成前面取到的块作废。
昆明湖参数的读法
Parameters.scala 中可看到当前基线含 numBr=2、FtqSize=64、IBufSize=48、DecodeWidth=6、CommitWidth=8。这些数字不能孤立理解:预测器每块表达能力、FTQ 在途深度、IBuffer 去耦能力和后端消费宽度共同决定前端供给。
历史长度由预测器配置推导:
val allHistLens = SCHistLens ++ ITTageTableInfos.map(_._2) ++
TageTableInfos.map(_._2) :+ UbtbGHRLength
val HistoryLength = allHistLens.max + numBr * FtqSize + 9
它不仅容纳最长相关历史,还为大量在途预测和实现余量留空间。
架构师练习
假设 workload 的 branch MPKI 从 4 降到 3,平均误预测代价 18 周期;另一方案保持 MPKI=4,但把恢复代价降到 14 周期。先用上式比较,再讨论两种方案对面积、频率和不同 workload 的稳健性。
真实测量清单
不要只收集总 IPC。至少同时记录:预测请求数、各级 override、FTB/TAGE/SC/ITTAGE/RAS miss、backend redirect latency、BPU update latency、FTQ occupancy、IBuffer full、ICache/ITLB miss 和前端空泡归因。