计算机体系结构 19:隐藏访存延迟
隐藏访存延迟
一次 cache miss 的原始延迟可能很长,但程序不一定完全停住。若后面还有独立 miss,非阻塞 cache 可以让多个请求同时在路上;若后面的地址依赖前一次 load 的结果,处理器只能等。预取则尝试在需求访问前把数据带来,但它也会消耗带宽、占 cache line,甚至挤掉后续需要的数据。
本篇的核心问题是:MSHR 怎样让独立 miss 重叠,同一 block 的 miss 怎样合并,依赖链为什么无法重叠;预取命中率之外还要统计什么流量和污染?核心案例使用两个 MSHR entry、10 周期 miss latency 和 next-line prefetch。证据等级为教学时序模型,关键反例由功能断言保护。
实验目录为 examples/computer-architecture/cache/。本文附件包括 mshr_prefetch.json.txt、cache_model.py.txt 和 run_cases.py.txt。
非阻塞 cache 保存“还没回来”的 miss
MSHR(Miss Status Holding Register)可以理解成未完成 miss 的记录。它保存目标 block、完成时刻和等待这个 block 的请求。gem5 的 classic cache 文档也把默认 cache 描述为带 MSHR 和 write buffer 的 non-blocking cache。
本文模型把每个新 block miss 占用一个 MSHR entry,10 个周期后完成。若新请求访问的 block 已经在 outstanding 表里,它不再发起第二个下层请求,而是 merge 到已有 miss,完成时刻相同。若 MSHR entry 已满,新 miss 必须等最早的 outstanding 完成。
这套模型没有端口、仲裁、写缓冲、乱序提交或 cache coherence。它只回答一个问题:当 miss latency 固定时,独立性和 entry 数怎样影响完成时间。
隐藏延迟的第一步是把“发出请求”和“等待返回”分离。模型用 outstanding[block] = done_time 保存未完成 miss;同一 block 的后续请求可以合并到已有完成时间,独立 block 则受 MSHR entry 数量限制。
独立 miss 和依赖 miss 的时间表
四个独立请求分别访问 0, 16, 32, 48,block size 为 16B,MSHR entry 数为 2。模型结果为:
| 请求 | block | issue | done | merged |
|---|---|---|---|---|
| ind-0 | 0 | 0 | 10 | false |
| ind-1 | 1 | 0 | 10 | false |
| ind-2 | 2 | 10 | 20 | false |
| ind-3 | 3 | 10 | 20 | false |
前两个 miss 同时在路上,后两个等 entry 腾出。总完成时间是 20,而不是 4 × 10 = 40。
依赖链使用同样四个地址,但每个请求的 ready time 依赖前一个结果。模型结果最后完成时刻为 40。MSHR entry 仍有 2 个,但没有独立工作可填满第二个 entry。
因此,内存级并行不是 cache 的单方面能力,而是硬件能力和程序依赖图的交集。数组分块、结构体布局、指针追逐和编译器调度都会改变可见的独立请求数量。
同一 block 的 miss 应当合并
请求 a 访问地址 0,请求 b 访问地址 4。二者属于同一 block。若 a 的 miss 尚未回来,b 不应再发一个相同 block 的下层请求;它可以挂在同一个 MSHR 上等待。
模型输出中 b.merged = true,done 与 a 相同。这一步很小,但它防止把“同一 cache line 内的多个 word”误计成多次下层流量。第 16 篇讲过 block 粒度;MSHR 也应按 block 合并,而不是按字节地址合并。
合并有边界。两个请求同 block 但权限、写入语义、原子操作或一致性状态不同,真实系统可能需要更复杂的处理。本文只覆盖普通读 miss 的教学场景。
预取要同时看 useful、useless 和 fill bytes
next-line prefetch 的规则很简单:每次 demand read 后,预取下一个 block。需求地址序列为 0, 16, 0,cache 为 2 sets × 1 way × 16B。
事件顺序可以压成六步:
| 步骤 | 事件 | 结果 | 影响 |
|---|---|---|---|
| 1 | read 0 | demand miss | 装入 block 0 |
| 2 | prefetch 16 | prefetch miss | 装入 block 1 |
| 3 | read 16 | demand hit | 首次消费预取,useful +1 |
| 4 | prefetch 32 | prefetch miss | 装入 block 2 |
| 5 | read 0 | demand miss | 逐出未消费的预取 block 2,useless +1;重新装入 block 0 |
| 6 | prefetch 16 | prefetch hit | block 1 仍在 cache 中,不产生 fill |
最终预取统计为 prefetches=3、useful_prefetches=1、useless_prefetches=1、fill_bytes=64。fill_bytes 包含 demand fill 和 prefetch fill;若只写命中率变好,就会漏掉下层带宽已经被预取消耗。
模型还保留两个反例。第一,预取 line 被 demand 首次命中后,prefetched 标记被清除,重复 demand 不会重复计 useful。第二,未消费的预取 line 被逐出后,再次装入同一 block 不会沿用旧的预取状态;否则会把后来的普通命中误算成预取收益。
预取收益需要和成本一起记录。本文案例的收益是 useful_prefetches=1;成本包括 fill_bytes=64、useless_prefetches=1,以及 block 2 被未使用就逐出的污染。只看 useful 数,会漏掉下层带宽和 cache 容量代价。
MLP、预取和乱序执行的关系
MSHR 提供硬件容器,乱序执行提供寻找独立工作的机会,预取提供提前发请求的猜测。三者可以叠加,也可以互相限制。若 MSHR 很少,预取可能占掉 demand miss 的 entry;若预取太激进,cache 被污染;若程序是严格依赖链,乱序窗口再大也找不到足够的独立 load。
第 12–14 篇讨论过乱序窗口、ROB 和 LSQ。本篇没有把 cache 模型接回那套 OOO 模型,只给出接口层面的理解:一个 load miss 发出后,后续能否继续推进,取决于是否还有已知地址、无真依赖、资源可用的操作。
这也解释了为什么“降低 miss latency”和“隐藏 miss latency”不是同一个优化。前者让每个 miss 更快回来;后者让等待期间有别的工作可做。性能报告应分清二者,否则同一个周期下降可能被错误归因。
复跑与验收
执行:
1 | |
本篇关注 outputs/mshr_prefetch.json。验收应检查:四个独立 miss 在两个 MSHR entry 下最后 done 为 20;四个依赖 miss 最后 done 为 40;同 block 的第二个请求 merged=true;预取统计中 fill_bytes=64、useful_prefetches=1、useless_prefetches=1;重复 demand 不重复计 useful,逐出后重装不沿用旧状态;模型拒绝 mshr_entries <= 0。
这些记录证明的是有限教学模型。它不等同 gem5 classic cache 的完整 MSHR 行为,不包含真实硬件预取器,也没有带宽计数器或性能计数器测量。
两道练习
练习一: miss latency 仍为 10,MSHR entry 从 2 增到 4。四个独立 miss 的完成时刻是多少?四个依赖 miss 呢?
解答:独立 miss 可以在 t=0 全部发出,完成时刻都是 10,最后 done 为 10。依赖 miss 仍要一个接一个,完成时刻为 10、20、30、40。entry 增加只帮助可并行的请求。
练习二: 某预取器产生 100 次预取,其中 80 次后来被 demand 命中。能否直接说它一定提升性能?
解答:不能。还要知道 100 次预取带来的 fill bytes、是否挤出 demand 会用的 line、是否占满 MSHR 或带宽、是否把关键 demand miss 推迟。高 useful 数说明方向可能对,但成本账没算完。
参考资料
- gem5:Classic Caches。2026-09-22 核查;支持 classic cache 默认含 MSHR、write buffer 和可启用 prefetch 的口径。
- gem5:Replacement Policies。2026-09-22 核查;用于说明 invalid line 与 LRU victim 选择的模型来源。
- 本系列 cache 教学模型。本文实际附件来自本地工作区当前输出,不声称 GitHub 链接已经包含本轮未提交文件。






