「30 天沉淀 90 mins」Day 1 CPU缓存一致性相关问题——MESI协议

发布时间 2023-08-21 23:09:12作者: Roshin

参考资料

  1. 小林Coding, 也是这里没想到居然讲了这个;

先简单复习一下冯诺依曼模型——运算器、控制器、存储器、输入设备、输出设备, 以及他们如何交互


寄存器分类:

  1. 通用寄存器,用来存放需要进行运算的数据,比如需要进行加和运算的两个数据。
  2. 程序计数器,用来存储 CPU 要执行下一条指令「所在的内存地址」,注意不是存储了下一条要执行的指令,此时指令还在内存中,程序计数器只是存储了下一条指令「的地址」。
  3. 指令寄存器,用来存放当前正在执行的指令,也就是指令本身,指令被执行完成之前,指令都存储在这里。

总线分类:

  1. 地址总线,用于指定 CPU 将要操作的内存地址;
  2. 数据总线,用于读写内存的数据;
  3. 控制总线,用于发送和接收信号,比如中断、设备复位等信号,CPU 收到信号后自然进行响应,这时也需要控制总线;

CPU缓存一致性

CPU缓存是什么?

CPU缓存又叫CPU Cache, 通常分为三级: L1, L2, L3 三级核心, L3所有核心共享;

明确点: CPU访问缓存明显快于访问内存

CPU缓存的结构, 由多个 Cache Line 组成, 对于单个 Cache Line 组成

| tags | Data Block |

修改缓存时可能会造成缓存与内存的不一致, 如何同步? 有两种方法, 写直达 (write through) , 写回 (write back)

写直达

  • 每次写数据, 同时写入缓存和内存, 如果缓存没有对应数据则直接写内存, 否则先改缓存再改内存
  • 性能损失大

写回

  • 当发生写操作时,新的数据仅仅被写入 Cache Block 里,只有当修改过的 Cache Block「被替换」时才需要写到内存中
  • 懒操作的思想, 先标记, 缓存需要被覆盖时再写内存, 显然性能更好

什么是CPU缓存一致性(Cache Coherence)

考虑 A,B 两个核心, 同时执行 i++ 的操作, 设 i 初始为 0, A执行后, 采用写回方式, A 内缓存 i 为 1, 而内存中为 0, 此时 B 执行 i++ 拿到的 i = 0 是错误的, 这就是缓存不一致的现象;

显然对于单CPU核心机器来说, 天生 CPU 缓存一致性, 因此仅考虑多CPU核心的情况

怎么保证CPU缓存一致性?

通过两点来解决:

  1. 某个 CPU 核心里的 Cache 数据更新时,必须要传播到其他核心的 Cache,这个称为写传播(Wreite Propagation);

  2. 某个 CPU 核心里对数据的操作顺序,必须在其他核心看起来顺序是一样的,这个称为事务的串形化(Transaction Serialization)。

保证事物串行化:

  • CPU 核心对于 Cache 中数据的操作,需要同步给其他 CPU 核心
  • 引入锁, 保证 CPU 核心对相同数据的缓存更新互斥

总线嗅探(Bus Snooping)

写传播的原则就是当某个 CPU 核心更新了 Cache 中的数据,要把该事件广播通知到其他核心, 常用 Bus Snooping 实现;

广播事件也有可能来自本地CPU核心

CPU 需要每时每刻监听总线上的一切活动,但是不管别的核心的 Cache 是否缓存相同的数据,都需要发出一个广播事件,这无疑会加重总线的负载。

显然 Bus Snooping 无法保证事物串行化, 需要引入 MESI 协议.

MESI

What is MESI?

  • Modified,已修改
  • Exclusive,独占
  • Shared,共享
  • Invalidated,已失效

进一步解释

  • E 和 S 都保证 Cache Block 里的数据时干净的(和内存一致), E 和 S 的区别是是否只有 1 个 Cache核心拥有这个数据的缓存;
  • 修改 S 的 Cache Line, 需要先广播到其他 core, 等待其他 core 标记数据对应 CacheLine 为 I, 之后才可以更新当前 Cache Line
    并标记为 M ;
  • 当 Cache Line 是** M 或者 E 的时候修改不需要广播到其他 CPU 核心**, 减少总线带宽压力

因此很容易给出一个状态机模型, 转移比较简单, 4 种触发事件:

  1. 本地读自己Cache (从 I 触发, 需要区分本地是否有数据决定是 E 还是 I)
  2. 本地写自己Cache
  3. 其他写它自己Cache
  4. 其他读它自己Cache

原子操作、CAS

MESI 确保了,同一数据在所有 Cache 中只会被 1 个 core 修改, 假设两个 core 并行修改且同时发出写入或读-修改(read-for-ownership, RFO)请求, 此时请求会在总线发生碰撞 “pingpong”, 总线和缓存协议通常有冲突解决策略,如基于优先级的算法或其他机制,来确定哪个核心可以首先获得修改权限。