June's Studio.

Golang-MESI协议

字数统计: 979阅读时长: 3 min
2022/11/22

基于失效的缓存一致性协议 (invalid-based)

状态

MESI对应4种状态

  • 已修改Modified(M)

    缓存行是脏的 ,与主存值不同。如果别的CPU内核要读主存这块数据,该缓存行必须回写到主存,状态变为共享(S)

  • 独占Exclusive(E)

    缓存行只在当前缓存中,但是干净的(clean),与主存中数据一致。当别的缓存读取它时,状态变为共享;当前写数据时,变为已修改状态。

  • 共享Shared(S)

    缓存行也存在于其它缓存中且是干净的。缓存行可以在任意时刻抛弃。

  • 无效Inivalid(I)

    缓存行是无效的

操作

状态转换的两种场景:

  • 缓存所在处理器的读写 & 其他处理器的读写
  • 总线请求被总线窥探器监视

处理器对缓存的请求:

  1. PrRd: 处理器请求一个缓存块
  2. PrWr: 处理器请求一个缓存块

总线对缓存的请求:

  1. BusRd: 窥探器请求指出其他处理器请求一个缓存块
  2. BusRdX: 窥探器请求指出其他处理器请求一个该处理器不拥有的缓存块
  3. BusUpgr: 窥探器请求指出其他处理器请求一个该处理器拥有的缓存块
  4. Flush: 窥探器请求指出请求回写整个缓存到主存
  5. FlushOpt: 窥探器请求指出整个缓存块被发到总线以发送给另外一个处理器(缓存到缓存的复制)

处理器操作带来的状态变化:
Invalid:
PrRead:
1.给总线发busRd信号 -> 其他处理器看到busRd,检查是否有有效的数据副本并通知发出请求的缓存,
如果有有效的副本,如果状态为M,需要先回写进内存,然后本地cache再从内存中读取数据,状态转为(S)Shared。
如果状态为S或E,则本地cache从内存中读取数据,并将这些缓存状态置为S
如果其他缓存都没有有效的副本,从内存中读取,本地缓存状态转为(E)Exclusive

PrWr:
给总线发BusRdx信号,转改转换为Modified。 如果其他缓存有有效副本,其中一个缓存发出数据,否则从主存中获得数据
如果其他缓存中有有效副本,见到busRdx信号后,本地缓存置为无效Invalid

MESI协议操作图解[5]

假定下述读/写操作访问同一主存位置的数据。操作流是 : R1, W1, R3, W3, R1, R3, R2. 最初所有缓存为空。 	

表 1.3 MESI工作举例 所有操作引用同一缓存行 (例如: "R3" 值处理器3的读操作)
本地 请求P1P2P3产生的

总线请求

数据提供者
0最初-----
1R1E--BusRdMem
2W1M----
3R3S-SBusRdP1's Cache
4W3I-MBusUpgr-
5R1S-SBusRdP3's Cache
6R3S-S--
7R2SSSBusRdP1/P3's Cache

总结

当cup去写数据时(M),如果操作的是共享变量(S),会发出信号通知其他cpu将该变量的缓存置为无效状态(I)。
因此当其他cpu需要读取这个变量时,发现自己缓存中该变量的缓存行是无效的,就会从内存中重新读取,确保一致性。

当读取数据时,其他cpu缓存行如果修改了数据,要先把数据写回主内存中
当修改数据时,其他cpu中该缓存行状态都必须失效

MESI 与内存屏障

MESI 如果简单粗暴地实现,会有两个很明显的性能问题:

  • 当尝试写入一个 Invalid 缓存行时,需要等待从其它处理器或主存中读取最新数据,有较长的延时
  • 将 cache line 置为 Invalid 状态也很慢
    因此 CPU 在实现时一般会通过 Store Buffer 和 Invalidate Queue 机制来做优化
CATALOG
  1. 1. 状态
  2. 2. 操作
  3. 3. 总结
  4. 4. MESI 与内存屏障