MESI协议
MESI协议是基于Invalidate的高速缓存一致性协议,并且是支持回写高速缓存的最常用协议之一
此外还有一些其他的缓存一致性协议比如:MSI,MOSI,Synapse,Firefly及DragonProtocol等等
主流的计算机cpu执行,简要流程
如下图所示:
执行数据加载的流程如下:
- 将程序和数据从硬盘加载到内存中去
- 将数据从内存中加载到CPU的缓存中去,大多为三级缓存(L3 -> L2 -> L1)
- CPU取缓存中的数据,加载到寄存器中,进行计算
- CPU将计算完成的数据刷新回缓存,在一定的条件会刷新回内存中去,保持数据的一致性
为什么需要缓存一致性协议
在现在CPU多为多核的情况下,每个核心有自己单独的缓存区,当多个核心一起操作多个线程对同一个数据进行操作时,如果在核心B在核心A把修改完之后的数据刷新回内存中去之前,进行了数据的操作,就会导致数据的不确定性。
总线加锁可以解决这种情况,但是在加锁的时候,在一个核心操作该数据完成前,其他核心也无法修改其他数据,这会导致CPU的性能严重下降。
缓存一致性协议只会对单个的缓存行进行类似加锁的操作,来进可能的保证数据的一致性
MESI的状态
MESI的四个字母就是标记缓存行的四种状态 M E S I
状态 | 描述 | 状态改变条件 |
---|---|---|
M 修改 (Modify) |
表示该缓存行有效,但是是被更改过的,与内存中的数据并不一致,并且该缓存行只存在于本缓存中 (举例:我们有三个CPU,每个CPU都有自己的缓存区域,那么M表示CPU-A从内存中取到了数据,其他CPU并没有取此数据(只存在于本缓存中),CPU-A还把该数据进行了更改,导致与内存中的源数据并不一致) |
当有其他试图读取该缓存在内存中的源数据时,它们须等待此缓存写回到内存,这时此缓存行的状态为S |
E 独占 (Exclusive) | 表示该缓存行有效,数据和内存中的一致(干净),并且数据只存在于本缓存行中 |
如果有相应的其他 读取 内存中关于本缓存行的操作时,会将自身的状态更改为S。 |
S 共享 (Shared) | 表示该缓存行有效,数据和内存中的一致,并且数据也同时存在于其他缓存中 | 如果其他缓存将该数据在它当前所在的缓存区域内将缓存修改,那么其他没有修改的缓存就需要把S状态修改为I状态 |
I 无效 (Invalid) | 该缓存行数据无效 |
那么接下来我们来看几张图来理解这几个状态:
M (修改)和 E (独占)的描述(不包括状态改变条件)
我们先只关心CPU-1 的操作步骤
- 我们先发出了一条指令,要读取内存中a0的数据
- 我们经过总线等步骤,来到了内存
-
我们从内存中取到了数据并放入到了缓存中,此时这个缓存行的状态为E (独占,只存在于当前数据行中,并且干净)
- CPU获得数据
-
我们这个时候选择了修改a0的数据,那么我们就会直接修改位于我们当下缓冲区中数据
此时我们修改成功,a0的数据状态成功的变为了M (缓存行有效,只存在于当前缓存行中,于内存中的不一致)
我们来看下内存中目前的a0数据:
S(共享)
- 我们直接用CPU-0、CPU-1、CPU-2 都发出了read a0的指令,那么此时它们三个的缓冲区都有此数据,并且都没有修改是干净的,那么它们的状态就都为S
M (修改)状态改变条件描述
此时我们先在CPU-1中读取并修改了a0所以它的目前状态为
M(修改)
接下来我们在CPU-0中读取位于内存中的a0数据,看看会发生什么?
- CPU-0发出read a0的指令,这个时候通过数据总线(ADDRESS BUS)对CUP-1的数据进行同步
- 第一步这个时候CPU-1中M状态的a0数据状态修改为S(共享)这个时候开始把数据与内存中的数据进行同步
第二步,这个时候内存中同步了被修改的数据,然后被CPU-0进行读取,并把S状态的数据放入自己的缓存,然后读取a0,执行完毕
E(独占)状态改变条件描述
此时只有CPU-1有a0这个数据,所以它是符合E(独占)的状态
接下来让我们再用CPU-0来读取a0来看看
不出所料当CPU-0读取数据的时候,它们就符合了共享的条件,那么它们的状态就会更改为S
S (共享) 状态改变条件描述
此时我们三个CPU都拥有a0的数据缓存,所以它们的状态都有S(共享),那么我们写入其中一个数据
当我们更改了CPU-1的数据时,我们会同步内存中的数据,使其保持最新,然后把其他CPU中的缓存数据状态置为I(无效、该缓存行无效)的状态
MESI的缺点
如果由特定块上的各种高速缓存执行连续读取和写入操作,则每次都必须将数据刷新到总线上。因此,主存储器将在每次冲洗时拉动它并保持清洁状态。但这不是一项要求,只是由于MESI的实施而导致的额外开销。 MOESI协议克服了这一挑战