“参考程序把 与缓存对象分开。 只改预测表并返回块号,不读内存、不创建 MSHR。 则把选中的合法块首字作为 的普通读取交给非阻塞缓存接口;每条提示可能 HIT、MERGE、MISS 或 RE…”
形式陈述
一次缺失已经发往下层,结果还没有回来。此时另一条读取恰好命中缓存,为什么也要跟着停住?若第三条读取需要同一条尚未返回的缓存行,又是否值得再发一次请求?非阻塞缓存把“当前正在服务哪次读取”拆成若干份未决记录,使这些情况能分别处理。
先固定一个可以完整证明的只读模型。下层内存是含 read(a) 的字节地址
至多有
参考接口按以下顺序作决定。每次调用是一个原子事件;驱动器可以在两次调用之间任意选择下一件事,但没有并发修改内部数组。
- HIT。 先查有效驻留行。命中就返回新请求身份及一个值,并更新最近使用时间。此分支不需要空闲 MSHR。
- MERGE。 未命中却有相同块的未决事务,且其目标列表未满:添加新请求,返回原事务身份,不再发第二次下层请求。目标满时返回
RETRY / target-full。 - MISS。 这是新块,且有空闲 MSHR 和未被保留的路:按替换策略选位置。本页先取编号最小的无效路;否则在合格驻留路中取最近使用时间最小者,平局取最小路号。旧行立即失效,这一路保留给新事务,直到填充结束。返回新请求与新事务身份。
- RETRY。 新块遇到全局 MSHR 满,返回
mshr-full;尚有 MSHR、但该组所有路都被保留,返回ways-reserved。这三种资源重试都不接收请求,也不改变任何状态,包括序号、替换次序和内部记录。
complete(t) 接受一个当前存活的、由本对象发出的原事务身份。它从可信的
保证是按请求身份而非按完成顺序给出的:每个已接收请求恰好返回一次,返回值为 RETRY 本身没有活性承诺,调用者若需要它最终成功,还须有重试公平性和资源最终可用的条件。
直觉
可以把 MSHR 看成一张“货还在路上”的登记单。上面除了货物编号,还写着谁在等、各自要哪一部分,以及货到以后放在哪个位置。新的顾客若要同一批货,只加入登记单;要已经在架上的货,则当场取走。
这里有两个常被混淆的容量。MSHR 数量限制正在等待多少个不同块;每条目标列表长度限制多少次读取可以等同一块。还有第三个约束:一个组的路都被占作填充位置时,即使全局仍有空闲 MSHR,也不能再接收映射到该组的新块。
正确性可以逐事件归纳。初始没有未决事务,所有行无效。假定事件前满足四条不变量:
- 每个未决块只有一条事务,每个被保留的路只有一个事务所有者
- 有效行不被保留,且数据逐字等于它所标记的内存块
- 每个尚未返回的已接收请求,恰在一条对应块的目标列表中
- 已返回请求不再留在任何目标列表中
HIT 只读正确的有效行,生成新身份并立即返回,不加入等待集合。MERGE 只往已找到的唯一同块列表添加新身份。MISS 在检查资源后才分配,选择的路没有所有者,因此不会破坏另一个事务的归属;旧驻留值失效后不可再被命中。complete 找到原身份的唯一记录,将可信整行安装到它仍拥有的位置,把整张目标列表移出等待集合并返回。释放记录以后,旧身份再也找不到,故不能重复响应。失败与重试不改状态,也保持不变量。
这份证明没有要求先来的事务先返回。内存不变,所以两个不同块的返回次序不会改变任何读取值。它也没有证明处理器能够越过任意数据依赖,更没有给寄存器提交、异常或多核可见性作保证;这里的调用者只是能够持有多个独立读取的事件驱动器。
例子与边界
取
先读取地址 0 并完成填充。随后读 16,建立块 1 的事务;读 0 立即 HIT;读 20 合并进块 1 的目标列表。再读 24 时目标已满,返回 target-full,不能悄悄丢掉此前等待者,也不能为块 1 再建一条事务。
接着读 32,建立第二条事务。现在再读新块首址 48 得到 mshr-full,但重读驻留地址 0 仍然可以命中。先完成块 2,再完成块 1,返回地址及值依次是
返回次序不同于接收次序,请求身份却不会混淆。包括预热在内,真正发往下层的事务只有三条,而不是每次未命中读取都发一条。
单独把配置改成 ways-reserved,不是 MSHR 不够。若错误地把保留路当成普通 LRU 牺牲品,两条事务会指向同一个物理位置;较晚的一次填充可能覆盖较早但尚未正确消费的数据,所有权归纳从这里断开。
本页没有写缓冲、脏行、取消、总线错误、地址转换、失效消息或缓存一致性。若内存会变化,仅凭不可变
推论与应用
完整参考程序中的 WholeLineCache 实现上述合同,六项任务要求分别交出接收、合并、重试和完成记录。返回批次交给调用者,缓存内部不保存随运行增长的完成日志。请求上的 origin 只区分 demand 与 prefetch 标签,不改变取值语义。
令
数据数组占 snapshot() 与 check() 遍历数据和目标,未参与每次正常转移。
以上按可容纳地址和计数器的字操作计费。参考程序的序号与时间戳单调增长,运行
非阻塞允许重叠等待,能否转化为更短程序时间还取决于独立请求、下层服务能力、资源冲突和真正的关键依赖。增加 MSHR 只能扩大可同时接收的集合,不能从安全不变量推出吞吐翻倍。
参考资料
- David Kroft, “Lockup-Free Instruction Fetch/Prefetch Cache Organization,” ISCA, 1981:原论文。未决状态、目标识别与乱序返回的历史来源;本文保留路策略和只读整行协议另行定义并证明。
- gem5 项目,Classic Caches 官方文档。说明非阻塞缓存中的 MSHR、写缓冲以及索引/替换分工;不作为本教学机的逐事件规范。