缓存一致性协议

现代CPU都是多核心+多级缓存架构,比方说我正在使用的这颗i5 6500,就有4颗物理核心,每颗核心独享32K(数据)+32K(指令)的一级缓存,独享256K的二级缓存,4颗核心共享6M的三级缓存

如果我们想要保证工作在不同核心上的线程读取到的数据都是一致的,最简单的做法是保证所有读写操作直接在内存上发生

如果真的这么做,会导致L1/L2/L3全部失效,而且所有读写操作全部都会落在内存总线上,效率极其低下

 

下面是一些改进方法

1. 有效位 valid bit

为每个cache line增加一个额外的bit,用于表示这个cache line的状态:valid/invalid,制定规则如下:

读:

1. 缓存没有命中(cache miss或者cache line状态为invalid),直接从内存读取数据+写入到cache+标记cache line为valid

2. 缓存命中 && cache line状态为valid,直接从cache中读取数据

写:

1. 向所有cache发送写信号

2. 如果有cache中有这条数据 && cache line状态为valid,将这个cache line的状态改为invalid

3. 将新数据写入到内存与cache,并将相关的cache line标记为valid

驱逐:

直接从cache中删除数据,无视cache line的状态

优点:减少了从主存中读取数据的次数

缺点:每次写操作都要刷回主存

 

2. MSI Modified-Shared-Invalid

Modified:这条cache line已被修改,其数据与主存中不一致,被标记为Modified的cache line在被驱逐时,需要把数据刷回到主存中

Shared:这条cache line与主存中的数据是一致的,并且至少在一个核心中存在,这条cache line的数据在被驱逐时无需做额外操作

Invalid:这块数据不存在,需要重新从主存中读取

对应的读写规则如下:

读:

1. 如果缓存不命中(cache miss或者cache line状态为invalid),询问其他cache是否含有这条数据且状态为Modified

    a. 如果有,要求其他cache把数据写回到内存 + 当前cache更新数据 + 两个cache的状态都被设为Shared。

    b. 如果没有,直接将数据写到cache中 + cache line标记为Shared

b. 如果缓存命中(cache line状态为Modified或者Shared),直接从缓存中读取数据

写:

1. 如果缓存不命中(cache miss或者cache line状态为invalid),询问其他cache是否含有这条数据且状态为Modified或者Shared

    a. 如果其他cache中有这条数据并且状态为Modified,要求其他cache把数据写回内存 + 当前cache获取最新数据并与想要写入的数据合并 + 当前cache line被标记为Modified + 其他cache line被标记为Invalid

    b. 如果其他cache中有这条数据并且状态为Shared,那么要求这些cache将对应的cache line设为Invalid + 当前cache取数据并将对应cache line标记为Modified

    c. 如果其他cache中不含有这条数据(或者含有但是状态为Invalid),当前cache直接取数据并将对应cache line标记为Modified

2. 如果缓存命中并且cache line的状态为Modified,直接写入数据

3. 如果缓存命中并且cache line的状态为Shared,要求其他cache将这条数据标记为Invalid,向本地cache line写入数据,并将状态修改为Modified

驱逐:

如果cache line状态为Modified,先将数据写回到内存

如果cache line状态为Shared或者Invalid,直接删除这条cache line就可以了

MSI协议的好处是,不是每一条write操作都需要直接写到内存(如果同一个核心对同一块内存做连续的write操作,那么这些write操作实际上都是在cache里进行的),减少了对内存写入的次数

同时我们又发现一个问题,如果我们想要write某条状态为Shared的cache line,需要先查询其他cache上是否也有这条数据,但实际上这条数据可能只被当前cache所占有。在这种情况下,其实无需做对其他cache的查询操作。于是我们可以对MSI协议做进一步的改进,再添加一个状态用于标记某个cache line中的数据是被当前cache独占的情况。

于是得到了MESI协议

 

MESI Modified-Exclusive-Shared-Invalid

与MSI协议相比,Invalid不变,Modified有所调整,Shared分裂成了Shared与Exclusive。

Modified:这条cache line只在当前cache中存在,而且是脏的(与主存中的数据不一致)。在这条cache line被回写到内存之前,禁止对内存中这条数据的read操作。在被回写到内存中之后,这条cache line被标记为Exclusive。

Exclusive:这条cache line只在当前cache中存在(独享),这条cache line的数据与主存中一致。如果这条cache line被其他cache读取,Exclusive会转为Shared。如果发生写入,Exclusive会转换为Modified

Shared:这条cache line可能在多个cache中存在,这个cache line的数据与主存保持一致,这条cache line可以随时被转化为Invalid。

Invalid:标记这条cache line是无用的

对应的读写规则如下:

读:

1. 如果缓存未命中(cache miss或者cache line状态为Invalid),查询其他cache中是否有对应的cache line处于Modified/Exclusive/Shared模式

    a. 如果其他cache中存在处于Modified状态的cache line,那么要求这条cache line刷回到主存,再保存到当前cache中,然后两条cache line的状态都设置为Shared

    b. 如果其他cache中存在处于Exclusive或者Shared状态的cache line,那么直接把这些cache line复制到当前cache,然后将所有的cache line都设置为Shared状态

    c. 如果其他cache中不存在Modified/Exclusive/Shared状态的cache line,那么从主存中读取数据写入到当前cache,并将这条cache line的状态设置为Exclusive

2. 如果缓存命中(cache line状态为Modified/Shared/Exclusive),那么直接从cache中读取数据

写:

1. 如果缓存未命中(cache miss或者cache line状态为Invalid),查询其他cache中是否有对应的cache line处于Modified/Exclusive/Shared模式

     a. 如果其他cache中存在处于Modified状态的cache line,那么要求其他cache中的cache line刷回到主存并修改状态为Invalid。这些数据会被保存到当前cache中,然后与写入的数据合并,当前cache中的cache line的状态为Modified

    b. 如果其他cache中存在处于Exclusive或者Shared状态的cache line,那么其他cache中的cache line都被标记为Invalid状态,当前cache中的cache line被标记为Modified状态

    c. 如果其他cache中不存在Modified/Exclusive/Shared状态的cache line,那么直接将当前cache中的cache line标记为Modified状态

2. 如果缓存命中(cache line的状态为Modified/Exclusive/Shared)

    a. 如果当前cache line的状态为Modify或者Exclusive状态,直接向cache line写入数据,并且将其标记为Modified状态

    b. 如果当前cache line的状态为Shared状态,那么发起命令,让其他cache中的Shared状态的cache line状态变更为Invalid状态,然后向当前cache中的cache line写入数据,并将其标记为Modified状态

驱逐:

1. 如果cache line状态为Modified,那么将数据写回到主存

2. 如果cache line的状态为Exclusive/Shared/Invalid,那么直接将这条cache line抛弃

MESI协议还是比较复杂的,但是它进一步减少了Cache与主存之间的交互操作

比方说在无竞争的先读后写的场景下(i = i++)

MSI协议:先从主存中读取i的值,这条cache line被标记为Shared。此时如果我们想要修改i,就需要广播查询其他cache中是否含有这条cache line(实际上这条cache line是独享的,没有必要做广播查询)

MESI协议:先从主存中读取i的值,这条cache line被标记为Exclusive,此时我们可以确定这条cache line只在当前cache中存在,所以就可以放心大胆的直接修改i,无需做广播查询操作。

 

参考文献 

https://zhuanlan.zhihu.com/p/25876351

https://en.wikipedia.org/wiki/MESI_protocol

https://yq.aliyun.com/articles/8061

posted @ 2017-04-26 21:05  stevenczp  阅读(1914)  评论(0编辑  收藏  举报