『30 天沉淀 90 mins』Day 1 CPU缓存一致性相关问题——MESI协议
参考资料
- 小林Coding, 也是这里没想到居然讲了这个;
先简单复习一下冯诺依曼模型——运算器、控制器、存储器、输入设备、输出设备, 以及他们如何交互
寄存器分类:
- 通用寄存器,用来存放需要进行运算的数据,比如需要进行加和运算的两个数据。
- 程序计数器,用来存储 CPU 要执行下一条指令「所在的内存地址」,注意不是存储了下一条要执行的指令,此时指令还在内存中,程序计数器只是存储了下一条指令「的地址」。
- 指令寄存器,用来存放当前正在执行的指令,也就是指令本身,指令被执行完成之前,指令都存储在这里。
总线分类:
- 地址总线,用于指定 CPU 将要操作的内存地址;
- 数据总线,用于读写内存的数据;
- 控制总线,用于发送和接收信号,比如中断、设备复位等信号,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缓存一致性?
通过两点来解决:
-
某个 CPU 核心里的 Cache 数据更新时,必须要传播到其他核心的 Cache,这个称为写传播(Wreite Propagation);
-
某个 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 种触发事件:
- 本地读自己Cache (从 I 触发, 需要区分本地是否有数据决定是 E 还是 I)
- 本地写自己Cache
- 其他写它自己Cache
- 其他读它自己Cache
原子操作、CAS
MESI 确保了,同一数据在所有 Cache 中只会被 1 个 core 修改, 假设两个 core 并行修改且同时发出写入或读-修改(read-for-ownership, RFO)请求, 此时请求会在总线发生碰撞 “pingpong”, 总线和缓存协议通常有冲突解决策略,如基于优先级的算法或其他机制,来确定哪个核心可以首先获得修改权限。