目录监听协议的核心思想为什么需要全局总序基准监听协议MSI添加 Exclusive 状态MESI添加 Owned 状态MOSI非原子性总线与分离事务总线总线互联网络的优化真实商业系统案例监听协议的现状与未来C 完整模拟代码1. 监听协议的核心思想1.1 什么是监听监听协议Snooping Protocol的核心只有一句话所有缓存一致性控制器以相同的顺序观察监听所有一致性请求并各自独立地做正确的事来维护一致性。类比生活场景想象一个教室里的广播系统 老师某个核心说「我要独占使用黑板」 所有同学其他核心都听到了这句话监听 每个人各自决定「好我把手里的黑板照片作废掉」 关键所有人听到的是同一句话顺序也相同传统监听协议通过有序广播网络如共享总线来传播请求保证每个控制器观察到的请求序列完全一致即存在一个全局总序Total Order。2. 为什么需要全局总序2.1 有全局总序时的正确场景下面用 ASCII 时序图展示核心 C1 和 C2 都想以 M 状态获取同一个块 A。时刻 C1 的视角 C2 的视角 LLC/内存的视角 ---- ---------- ---------- --------------- 0 A:I A:I A:I内存是所有者 1 A:GetM(C1) A:GetM(C1) A:GetM(C1) - C1得到M - A:M - A:I - 内存失去所有权 2 A:GetM(C2) A:GetM(C2) A:GetM(C2) - C2得到M - A:I - A:M - 内存仍无所有权三者都看到同样的顺序C1 的 GetM 在前C2 的 GetM 在后所以各自状态机推进一致不会出现两个人同时处于 M 状态的情况。2.2 没有全局总序时的错误场景时刻 C1 的视角 C2 的视角 LLC/内存的视角 ---- ---------- ---------- --------------- 0 A:I A:I A:I 1 [C1先看到C1] [C2先看到C2] [先看到C1] A:GetM(C1) A:GetM(C2) A:GetM(C1) - A:M - A:M 结果C1 和 C2 同时认为自己是 M 状态 违反了 SWMR 不变式C1 和 C2 同时处于 M 状态意味着两个核心可以同时写同一个块这是严重的一致性错误。2.3 全局总序与内存一致性模型的关系全局总序不仅保证一致性还对内存一致性模型SC、TSO有影响。考虑两个块 A 和 B 的场景无全局总序时可能产生违反 SC/TSO 的执行C1 执行store A1store B1 C2 执行r1load Br2load A 期望SC/TSO如果 r11那么 r2 也应该1 实际无总序r11r20 -- 违反有了全局总序后C2 在看到 B 的新值之前必然先看到 A 的无效化请求所以再次读 A 时只能得到新值结果满足 SC/TSO。2.4 序列化点Serialization Point总线仲裁逻辑就是序列化点多个控制器同时发请求 | v [总线仲裁逻辑] -- 序列化点决定哪个请求先上总线 | 只有一个请求被允许上总线 | v 所有控制器都监听到这个请求关键点请求在序列化点被排序的那一刻事务就逻辑上完成了即使数据响应还没到达请求者。这与乱序执行处理器中指令按程序序提交的概念类似。2.5 响应消息不需要总序监听协议只要求请求消息有总序响应消息携带数据则不需要广播不需要有序可以走独立的、更廉价的网络这是一个重要的优化机会数据消息体积大如果走广播总线会浪费带宽走单独网络可以大幅提升性能。3. 基准监听协议MSI3.1 协议概述MSI 协议只有三个稳定状态状态含义可读可写谁是所有者MModified已修改独占是是本缓存SShared共享只读是否LLC/内存IInvalid无效否否—使用写回缓存Write-Back Cache写操作只写到本地缓存驱逐时才写回内存。3.2 简单系统模型原子请求 原子事务原子请求Atomic Requests请求发出的同一周期内就被排序不可能有其他请求插进来。原子事务Atomic Transactions一个事务请求响应完全结束后下一个针对同一块的请求才能出现在总线上。原子总线示意时间从左到右 地址总线: [请求1] [请求2] [请求3] 数据总线: [响应1] [响应2] 特点必须等响应1完成请求2才能上总线 缺点总线利用率低等待时间长3.3 MSI 状态转换图缓存控制器Own-GetSOwn-GetMOwn-GetMOther-GetM 或 静默替换Other-GetSOther-GetM 或 Own-PutMI无效S共享只读M已修改3.4 MSI 状态转换图内存控制器GetMGetS 或 PutMIorS内存是所有者M某缓存是所有者3.5 过渡状态说明由于发出请求和收到响应之间存在时间差需要过渡状态过渡状态含义ISDI - S已收到自己的 GetS等 DataIMDI - M已收到自己的 GetM等 DataSMDS - M已收到自己的 GetM等 DataISADI - S已发 GetS等总线确认非原子请求时IMADI - M已发 GetM等总线确认SMADS - M已发 GetM等总线确认MIAM - I已发 PutM等总线确认IIAI - I中间态先把块给别人了等自己的 PutM 被确认3.6 运行示例非原子请求 原子事务初始状态C1IC2ILLCIorS 周期 C1 C2 LLC 总线请求 总线数据 1 发GetS/ISAD 2 发GetM/IMAD 3 GetS(C1) 4 -ISD 发Data给C1/IorS 5 Data(LLC-C1) 6 复制到缓存/S GetM(C2) 7 -I -IMD 发Data给C2/M 8 Data(LLC-C2) 9 复制/M 10 发GetS/ISAD 11 GetS(C1) 12 -ISD 发数据给C1和LLC/S -IorSD 13 Data(C2-C1) 14 复制/S 复制/IorS3.7 S 状态的静默驱逐S 状态的块被驱逐时不需要发任何消息静默驱逐因为其他缓存的行为不因此改变LLC/内存仍然持有有效副本但 M 状态的块驱逐时必须发 PutM 并把数据写回内存因为 M 状态的缓存是唯一持有有效数据的地方。4. 添加 Exclusive 状态MESI4.1 为什么要加 E 状态考虑一个常见场景MSI 协议下 核心读块 A -- 发 GetS -- 得到 S 状态 核心写块 A -- 发 GetM -- 得到 M 状态 共计2 次事务2 次总线占用 MESI 协议下如果读时没有其他人持有该块 核心读块 A -- 发 GetS -- 得到 E 状态独占干净 核心写块 A -- 静默升级 E - M无需总线通信 共计1 次事务节省了 50% 的总线带宽E 状态的条件E 状态 ⇔ 只有我有这个块 ∧ 内存副本是最新的 \text{E 状态} \Leftrightarrow \text{只有我有这个块} \wedge \text{内存副本是最新的}E状态⇔只有我有这个块∧内存副本是最新的4.2 如何知道只有我有有两种方案MESI 协议采用方案二保守 S 状态方案一总线上添加一根共享者信号线wired-ORGetS 发出时其他持有 S 的缓存拉高信号线请求者通过信号线判断是否有共享者。缺点需要额外物理线路。方案二采用LLC 保守地跟踪共享状态LLC 处于 I 状态 肯定没有其他缓存持有 响应时标记为独占数据LLC 处于 S 状态 可能有其他缓存持有保守估计 响应普通数据保守的意思是即使最后一个 S 副本被静默驱逐LLC 也不会立即知道它会继续保持 S 状态一段时间错过一些可以给 E 的机会但这没有正确性问题。4.3 MESI 状态转换图缓存控制器Own-GetS 且 LLC在I态Own-GetS 且 LLC不在I态Own-GetMOwn-GetMOther-GetM 或 静默替换静默升级Other-GetSOther-GetM 或 Own-PutMOther-GetSOther-GetM 或 Own-PutMI无效S共享只读E独占干净M已修改4.4 MESI 状态转换图内存控制器GetSGetMGetS 或 GetMGetS 或 PutMGetMI无共享者S有共享者EorM某缓存独占或已修改4.5 MESI 运行示例初始状态C1IC2ILLCI注意LLC 是 I 而不是 IorS 周期 C1 C2 LLC 总线 1 发GetS/ISAD 2 发GetM/IMAD 3 GetS(C1) 4 -ISD 发独占Data给C1/EorM 5 独占Data(LLC-C1) 6 复制/E独占 GetM(C2) 7 S -IMD -MD 8 Data(C1-C2) 9 复制/M 10 发GetS/ISAD 11 GetS(C1) 12 -ISD 发Data给C1和LLC/S -SD 13 Data(C2-C1) 14 复制/S 复制/S 对比MSI步骤6时C1得到E而不是S步骤7时C2的GetM 不需要等内存响应直接从C1的缓存获得数据。