MESI
Outlinks (0)
No outlinks found
Backlinks (0)
No backlinks found
1 · MESI#
MESI 是多核 CPU 中最经典的 cache coherence protocol(缓存一致性协议) 之一,用来保证不同核心看到的同一块内存不会各说各话。
1.1 · S 什么场景#
现代 CPU 每个核心都有自己的 L1/L2 cache。这样读写很快,但也带来一个根本问题:
- 核心 A 读过变量
x,把 cache line 缓存在本地 - 核心 B 也读过
x,也缓存了一份 - 如果 B 后来修改了
x - A 本地 cache 里还是旧值
只要多个核心会同时缓存同一个 cache line,就必须回答:
- 谁可以写?
- 其他核心的旧副本什么时候失效?
- 数据什么时候写回内存?
MESI 就是在这个场景下出现的。
1.2 · C 什么冲突#
冲突在于:缓存的本地性 和 共享数据的一致性 天然对立。
- 想要性能,就希望每个核心把数据留在自己 cache 里,少访问主存
- 想要正确性,就要求所有核心对同一地址的观察结果一致
如果没有一致性协议,会出现:
- 一个核心写了数据,另一个核心继续读旧值
- 多个核心同时认为自己可以写同一 cache line
- 内存、核心 A cache、核心 B cache 三者状态不一致
所以协议必须在“尽量少同步”和“绝不能读脏旧数据”之间找平衡。
1.3 · Q 什么问题#
1.3.1 · 1. MESI 四个状态分别是什么?#
MESI 是四种 cache line 状态的首字母:
M = Modified这行数据只在当前核心 cache 中,且被修改过,和主存不一致;将来被替换时需要写回E = Exclusive这行数据只在当前核心 cache 中,但没有被修改,和主存一致;当前核心后续写它时,可以直接升级为MS = Shared这行数据可能同时存在于多个核心 cache 中,内容和主存一致;读可以,写之前必须先让别人的副本失效I = Invalid本地这行 cache line 无效,不能使用;需要重新从别处获取
一个很实用的记忆方式:
M/E:当前核心“独占”S:多个核心“共享只读”I:没有这份有效数据
1.3.2 · 2. 它到底解决的是什么一致性问题?#
它解决的是 cache coherence,不是更高层的 memory consistency model。
- coherence 关心:
同一个地址的值,在多个 cache 之间怎么保持一致 - consistency 关心:
多个地址的读写,在程序语义上允许怎样重排
所以 MESI 回答的是:
当多个核心缓存了同一个 cache line 时,如何保证写入后其他核心不会继续使用过期副本?
1.3.3 · 3. 典型的读写流程怎么走?#
假设一开始所有核心对某个 cache line 都是 I。
1.3.3.1 · 场景 1:核心 A 读#
- A 发生 cache miss,发出读请求
- 如果别的核心没有这行,A 拿到数据后通常进入
E - 如果别的核心也有这行,A 通常进入
S
1.3.3.2 · 场景 2:核心 A 在 E 上写#
- 因为它本来就独占,且和别人不共享
- 可以直接从
E -> M - 不需要先通知别人失效
这也是 E 状态存在的意义:优化“先读后写”的常见路径。
1.3.3.3 · 场景 3:核心 A 在 S 上写#
- A 必须先发出失效请求(invalidate)
- 其他持有这行
S的核心把自己的副本改成I - A 获得独占写权限后,
S -> M
1.3.3.4 · 场景 4:别的核心 B 读取 A 的 M#
- A 持有的是最新值
- 协议需要让这份最新数据对 B 可见
- 常见效果是:A 提供/回写数据,自己的状态降级,B 拿到共享副本
- 最终常见变为
A: S, B: S
1.3.3.5 · 场景 5:别的核心 B 想写 A 的 M 或 S#
- B 必须先获得独占权
- A 的那份副本会被置为
I - B 成为新的
M
本质上,任意时刻一个 cache line 最多只能有一个 writer。
1.3.4 · 4. MESI 依赖什么机制工作?#
常见实现依赖 bus snooping(总线嗅探) 或目录协议。
以 snooping 为例:
- 一个核心发出读/写意图
- 其他核心监听这些一致性事件
- 如果发现自己缓存了同一行,就按协议做状态迁移
也就是说,cache 不只是存数据,还会对外部事件作出反应。
1.3.5 · 5. 为什么 false sharing 会很贵?#
MESI 的一致性粒度通常不是变量,而是 cache line。
这意味着:
- 线程 A 改变量
x - 线程 B 改变量
y - 即使
x和y是两个完全独立的变量 - 只要它们落在同一个 cache line 上
两个核心还是会反复触发:
- 失效对方副本
- 重新获取独占权
- cache line 在不同核心之间来回“乒乓”
这就是 false sharing。逻辑上没共享,硬件上却共享了同一行缓存。
1.4 · A 回答问题#
一句话总结:
MESI通过给每个 cache line 维护Modified / Exclusive / Shared / Invalid四种状态,保证多核缓存下“同一时刻最多一个写者,读者不会长期持有旧副本”。
最关键的理解点:
MESI管的是 cache line 一致性,不是语言级并发语义E状态的价值是减少“先读后写”的额外广播成本- 从
S变成可写,必须先让其他副本失效 M表示当前 cache 持有唯一且最新的数据副本false sharing本质上是 cache line 粒度一致性带来的副作用
如果是从工程角度理解它,最有用的结论通常只有两个:
- 多线程性能问题,很多时候不是锁太慢,而是 cache line 在打架
- 做并发数据结构或性能优化时,要同时考虑“共享变量”与“共享 cache line”