#MESI

为什么需要缓存一致

目前主流电脑的 CPU 都是多核心的,多核心的有点就是在不能提升 CPU 主频后,通过增加核心来提升 CPU 吞吐量。每个核心都有自己的 L1 Cache 和 L2 Cache,只是共用 L3 Cache 和主内存。每个核心操作是独立的,每个核心的 Cache 就不是同步更新的,这样就会带来缓存一致性(Cache Coherence)的问题。

举个例子,如图:

有 2 个 CPU,主内存里有个变量x=0。CPU A 中有个需要将变量x1。CPU A 就将变量x加载到自己的缓存中,然后将变量x1。因为此时 CPU A 还未将缓存数据写回主内存,CPU B 再读取变量x时,变量x的值依然是0

这里的问题就是所谓的缓存一致性问题,因为 CPU A 的缓存与 CPU B 的缓存是不一致的。

如何解决缓存一致性问题

通过在总线加 LOCK 锁的方式

在锁住总线上加一个 LOCK 标识,CPU A 进行读写操作时,锁住总线,其他 CPU 此时无法进行内存读写操作,只有等解锁了才能进行操作。

该方式因为锁住了整个总线,所以效率低。

缓存一致性协议 MESI

该方式对单个缓存行的数据进行加锁,不会影响到内存其他数据的读写。

在学习 MESI 协议之前,简单了解一下总线嗅探机制(Bus Snooping)。要对自己的缓存加锁,需要通知其他 CPU,多个 CPU 核心之间的数据传播问题。最常见的一种解决方案就是总线嗅探。

这个策略,本质上就是把所有的读写请求都通过总线广播给所有的 CPU 核心,然后让各个核心去“嗅探”这些请求,再根据本地的情况进行响应。MESI 就是基于总线嗅探机制的缓存一致性协议。

MESI 协议的由来是对 Cache Line 的四个不同的标记,分别是:

状态
状态
描述
监听任务
Modified 已修改 该 Cache Line 有效,数据被修改了,和内存中的数据不一致,数据只存在于本 Cache 中 Cache Line 必须时刻监听所有试图读该 Cache Line 相对于主存的操作,这种操作必须在缓存将该 Cache Line 写回主存并将状态改为 S 状态之前,被延迟执行
Exclusive 独享,互斥 该 Cache Line 有效,数据和内存中的数据一直,数据只存在于本 Cache Cache Line 必须监听其他缓存读主存中该 Cache Line 的操作,一旦有这种操作,该 Cache Line 需要改为 S 状态
Shared 共享的 该 Cache Line 有效,数据和内存中的数据一直,数据存在于很多个 Cache 中 Cache Line 必须监听其他 Cache Line 使该 Cache Line 无效或者独享该 Cache Line 的请求,并将 Cache Line 改为 I 状态
Invalid 无效的 该 Cache Line 无效

整个 MESI 的状态,可以用一个有限状态机来表示它的状态流转。需要注意的是,对于不同状态触发的事件操作,可能来自于当前 CPU 核心,也可能来自总线里其他 CPU 核心广播出来的信号。我把各个状态之间的流转用表格总结了一下:

当前状态
事件
行为
下个状态
M Local Read 从 Cache 中读,状态不变 M
M Local Write 修改 cache 数据,状态不变 M
M Remote Read 这行数据被写到内存中,使其他核能使用到最新数据,状态变为 S S
M Remote Write 这行数据被写入内存中,其他核可以获取到最新数据,由于其他 CPU 修改该条数据,则本地 Cache 变为 I I
当前状态
事件
行为
下个状态
E Local Read 从 Cache 中读,状态不变 E
E Local Write 修改数据,状态改为 M M
E Remote Read 数据和其他 CPU 共享,变为 S S
E Remote Write 数据被修改,本地缓存失效,变为 I I
当前状态
事件
行为
下个状态
S Local Read 从 Cache 中读,状态不变 S
S Local Write 修改数据,状态改为 M,其他 CPU 的 Cache Line 状态改为 I M
S Remote Read 数据和其他 CPU 共享,状态不变 S
S Remote Write 数据被修改,本地缓存失效,变为 I I
当前状态
事件
行为
下个状态
I Local Read 1. 如果其他 CPU 没有这份数据,直接从内存中加载数据,状态变为 E;
2. 如果其他 CPU 有这个数据,且 Cache Line 状态为 M,则先把 Cache Line 中的内容写回到主存。本地 Cache 再从内存中读取数据,这时两个 Cache Line 的状态都变为 S;
3. 如果其他 Cache Line 有这份数据,并且状态为 S 或者 E,则本地 Cache Line 从主存读取数据,并将这些 Cache Line 状态改为 S
E 或者 S
I Local Write 1. 先从内存中读取数据,如果其他 Cache Line 中有这份数据,且状态为 M,则现将数据更新到主存再读取,将 Cache Line 状态改为 M;
2. 如果其他 Cache Line 有这份数据,且状态为 E 或者 S,则其他 Cache Line 状态改为 I
M
I Remote Read 数据和其他 CPU 共享,状态不变 S
I Remote Write 数据被修改,本地缓存失效,变为 I I
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×