Lec 13目录式缓存一致性(Directory-Based Cache Coherence)
Lec 13 目录式缓存一致性(Directory-Based Cache Coherence)
MIT 6.5900 Fall 2024 · Daniel Sanchez 主题:目录式 vs 侦听式、MSI 目录协议、MSHR、目录组织与不精确表示、协议竞争与死锁、多级层级与原子操作
TL;DR
- 目录式可扩展架构:通过目录跟踪私有缓存共享者列表,将全局广播替换为点对点消息,使一致性协议能够扩展至成百上千核。
- MSI 状态协同与 MSHR:目录维护 Uncached、Shared、Exclusive 三态并处理权限降级与作废;未命中状态保持寄存器(MSHR)并发跟踪在途访存事务。
- 目录组织与死锁防范:采用稀疏全映射或有限指针等不精确表示平衡面积开销;物理网络上划分请求与响应的独立虚拟网络(Virtual Networks)彻底消除协议死锁。
一、维持一致性的充分条件
只要硬件保证:
- 任一时刻只有一个处理器对某位置有写权限;
- 写之后没有处理器能 load 该位置的陈旧副本。
一种正确做法:
- 写请求:写执行前,在所有其他私有缓存中作废该地址;
- 读请求:若某私有缓存有脏副本,先写回再读内存。
二、目录式 vs 侦听式 [Censier and Feautrier, 1978]
| 侦听式(Snoopy) | 目录式(Directory) | |
|---|---|---|
| 请求方式 | 在内存总线上广播 | 只向可能持有该行的缓存发消息 |
| 可扩展性 | 难扩展到大量处理器 | 可扩展到大量处理器 |
| 额外开销 | 需对缓存 tag 的额外侦听带宽 | 需额外的目录存储来跟踪可能的共享者 |
三、MSI 目录协议
-
缓存状态:M / S / I;
-
目录状态:
- Uncached(Un):无共享者;
- Shared(Sh):一个或多个共享者有读权限(对应 S);
- Exclusive(Ex):单一共享者有读写权限(对应 M)。
-
为清晰起见瞬态未画;先假设无竞争请求。
目录每项记录:Tag | State | Sharers(共享者集合)。
缓存端的转换
- 由处理器访问触发:
PrRd/ShReq(I→S)、PrWr/ExReq(I→M 或 S→M); - 由目录请求触发:
InvReq/InvResp(作废,M 态附带数据、S 态不带数据)、DownReq/DownResp(降级,M→S 附带数据); - 由逐出触发:
Eviction/WbReq(M 态附带数据写回,S 态不带数据)。
目录端的转换
由数据请求触发:
Un --ShReq--> Sh : Sharers = {P}; ShResp
Sh --ShReq--> Sh : Sharers += {P}; ShResp
Un --ExReq--> Ex : Sharers = {P}; ExResp
Sh --ExReq--> Ex : Inv(Sharers - {P}); Sharers = {P}; ExResp
Ex --ShReq--> Sh : Down(Sharer); Sharers = Sharer + {P}; ShResp
Ex --ExReq--> Ex : Inv(Sharers); Sharers = {P}; ExResp
由写回请求触发:WbReq 时按共享者数更新(>1 则移除该 P,==1 则清空),WbResp。
协议示例要点
- LD(读):私有缓存发
ShReq,目录置Sh {P}并ShResp回数据; - ST(写)且原为 Sh:发
ExReq,目录向其他共享者发InvReq,收齐InvResp后置Ex {P}并ExResp; - 逐出 M 行:发
WbReq(附数据),目录写回主存并置Un。
一个核先写回 0xA 再请求 0xB,两者会被串行化(受同一 MSHR/通道约束)——可考虑分离资源来并行化。
四、缺失状态保持寄存器(Miss Status Holding Register, MSHR)
MSHR 保存缓存之外的 load 缺失与写。
- 逐出/写回时:无空闲项则停顿;分配新项;通道可用时发
WBReq与数据;收WBResp后释放; - load 缺失时:先在 MSHR 中找匹配地址。未找到则(无空闲项停顿,否则)分配新项;找到则只填入对应 ld/st 槽;发
ShReq(或ExReq),收响应后把数据转发给 CPU 与缓存,再释放; - 每项含多个 ld/st 槽:允许用一个项服务对同一块的多个请求。
五、目录组织
需求:目录要跟踪共享某块的所有核。挑战:每块存共享者列表的空间随可能共享者数增长。
1. 平坦的、基于内存的目录(Flat, Memory-based)
为每行用主存中的若干位存状态与共享者,用位向量编码共享者。
简单;但慢,且处理器多时极低效(约 P 位/行)。
2. 稀疏全映射目录(Sparse Full-Map)
只需跟踪在私有缓存中的行。把目录组织成一个缓存(含 Line Address / State / Sharer Set,多路组相联)。
优点:低延迟、节能。缺点:位向量随核数增长 → 面积扩展差;相联度有限 → 目录诱发的作废。
目录诱发的作废(Directory-Induced Invalidations):为保持包含性(inclusion),把某项复用给新地址前,必须作废该项的所有共享者。
3. 共享者集合的不精确表示
- 粗粒度位向量(如 1 位代表 4 个核);
- 有限指针(Limited pointers):只维护少量共享者指针,溢出时标记"全部"并广播(或作废一个共享者);
- 允许假阳性(如 Bloom filter)。
优点:减少面积与能量。缺点:开销仍不可扩展(只是调常数因子);不精确的共享者 → 广播、作废或多余的作废/降级。
六、协议竞争与延迟优化
协议竞争(Protocol Races)
目录对同地址的多请求串行化(排队或 NACK 重试),但冲突请求仍有竞争。
升级竞争(Upgrade race):缓存 0 与 1 同时对 0xA 发
ExReq;目录先服务 0 的请求、把 1 的排队;缓存 1 本期望ExResp却收到InvReq→ 它应从S→M转为I→M并发InvResp。
额外跳数与三跳协议(3-Hop)
- 问题:别的私有缓存中的数据要经目录中转,增加延迟;
- 优化:让持有者直接把数据转发给请求者(如
ExFwd+ExResp+ExAck),减少跳数。
多级层级中的一致性
- 可在多级用相同或不同协议;关键不变式:在所有中间层级保证足够的权限;
- 例:8-socket Xeon E7(每 socket 8 核)—— socket 内用 MESI + L3 内嵌目录,socket 间用 MESIF + 侦听(QPI)。
内嵌缓存目录(In-Cache Directories)
把目录信息嵌入共享末级缓存(LLC)的 tag(共享缓存必须包含私有缓存内容)。
优点:避免独立 tag 开销与单独查找。缺点:当共享缓存 ≫ 私有缓存总和时可能低效。
避免协议死锁
即使网络无死锁,协议也可能死锁(如两节点用请求塞满所有中间缓冲,阻塞响应进入网络)。
解法:分离虚拟网络(不同虚通道与端点缓冲,共享同一物理路由器与链路)。多数协议至少需 2 个虚拟网络(请求与回复),常需 >2。
七、同步与原子操作
实现同步需原子读-改-写(如 swap)。一致性协议会让 mutex 在各核私有缓存间反复乒乓;可用 test&test&set 减少。
实现原子指令的选项
- 侦听式:锁总线 → 昂贵;
- 目录式:锁住缓存中的行(原子操作完成前禁止作废/逐出)→ 复杂;
- 现代处理器用 load-reserve / store-conditional。
Load-reserve & Store-conditional
在目录式分布式共享内存系统中,传统的「总线加锁」原子操作完全失效。现代体系结构普遍采用一对协作指令——保留加载(Load-Reserve, LR) 与 条件存储(Store-Conditional, SC),在无锁总线的前提下通过缓存一致性状态机实现乐观并发原子操作。
硬件为每个处理器核心配备专用的预约寄存器(Reservation Register),包含有效标志位(Valid Flag)与被监控的物理地址(Address)。其原子操作分三步推进:
- 发起预约(Load-Reserve):核心读取目标地址的数据写入通用寄存器,同时在硬件预约寄存器中记录该地址并将有效标志位置 1。此时私有缓存只需以共享态(Shared)持有该缓存行即可。
- 侦听冲突:在 LR 与 SC 之间的执行窗口内,本地缓存控制器持续监控来自互连网络的一致性消息。一旦收到来自目录的针对该预约地址的作废请求(
InvReq)或降级请求(表明其他核抢先获得了独占写权限),硬件自动将预约有效标志位强行清零。 - 条件提交(Store-Conditional):核心尝试将新值写入该地址。硬件首先原子校验预约寄存器的状态:若有效标志位仍为 1,则向目录申请独占写权限,完成数据写入并将状态标记为成功(
status = succeed);若有效位已被清零,则直接放弃写入并将状态标记为失败(status = fail),由软件循环重新尝试。
Load-reserve R, (a): Store-conditional (a), R:
<flag, adr> ← <1, a> if <flag, adr> == <1, a>
R ← M[a] then 取消其他处理器对 a 的预约; M[a] ← R; status ← succeed
else status ← fail
通过这一硬件机制,自旋锁等同步原语可被精简重构为纯粹的重试循环:
# Swap(R1, mutex):
L: Ld-Reserve R2, (mutex)
St-Conditional (mutex), R1
if (status == fail) goto L
R1 <- R2
相比于传统原子指令,LR/SC 实现了读写解耦:所有竞争获锁的核在自旋阶段均以共享只读方式读取锁变量,绝不触发写失效广播;只有在锁被释放时才会有核心执行 SC 尝试获取独占权,彻底消除了无谓的缓存行乒乓与互连网络带宽饱和。
目录式缓存一致性与协议设计总结
- 目录式一致性只向相关私有缓存发消息,以额外目录存储换取可扩展性,目录是排序点,支持无序网络;
- MSI 目录协议用目录状态 Un/Sh/Ex 与共享者集合,配 InvReq/DownReq/WbReq 等消息;MSHR 管理在途缺失与写;
- 目录组织在面积与精度间权衡:平坦位向量 → 稀疏全映射 → 不精确表示(粗粒度/有限指针/Bloom);
- 需处理协议竞争(如升级竞争)、用三跳转发降延迟、分离虚拟网络避死锁;原子操作用 LR/SC 实现。
在构建多级目录式缓存系统时,末级缓存(LLC)与私有 L1/L2 缓存之间的包含性属性(Inclusion Property)是架构师必须面临的核心决断:
- 严格包含(Strict Inclusion):LLC 必须容纳所有私有 L1/L2 中的行,此时目录信息可以直接嵌入在 LLC 的 Tag 阵列中,消除了独立目录的面积与访问开销。然而其致命代价在于「逆向作废(Back Invalidation)」——当 LLC 发生容量冲突需要逐出某行时,必须向私有 L1/L2 强行发送作废消息;一旦多核并发工作集庞大,LLC 的相联度瓶颈将引发剧烈的跨核颠簸,严重杀伤私有缓存命中率。
- 非包含 / 独占(Non-Inclusive / Exclusive):如现代 AMD Zen 架构,LLC 并不强制包含私有 L2。虽然避免了逆向作废,但系统必须配备独立的稀疏目录(Sparse Directory)或探测过滤网(Probe Filter),其表项容量必须足以覆盖全部私有缓存行。若稀疏目录发生冲突逐出,同样会触发目录诱动作废(Directory-Induced Invalidation)。因此,包含性选择本质上是在「Tag 硅片面积开销」与「跨层级作废流量」之间进行的一场高难度微架构博弈。 :::
下一讲:一致性与松弛内存模型