Cache一致性协议的实现方式为,在Cache中每一个Cache行设置一致性状态来记录该Cache行的读写状态,确保Cache行不会被多个处理器核同时修改,每个处理器核有足够信息得到本地Cache行的状态,并且在发出读写请求时,能够根据Cache行的状态进行下一步操作。
Mesi协议就是这样一个在多核处理器中保证Cache一致性的协议,它的逻辑密度非常的大,本人愚钝,几乎它的每一个状态切换都要思考很久才能理解,所以不得不尝试寻找一些工具来加速对MESI协议模型的理解,功夫不负有心人,还真被我找到了,这里共享出来,工具链接如下:
这个页面以动画的形式展现了MESI协议的工作原理。
页面提供了一个MESI协议的完整的状态机,有了它,我们就知道此刻在那个状态,条件是什么,下一步去那里,结合动画,大大减少了MESI协议的理解难度。图中的四个状态分别表示:
MODIFIED:M状态,该缓存行的数据只在本CPU的私有高速缓存中进行了缓存,而其他CPU中没有,是被修改过的(Dirty),即与主存中的数据不一致,且没有更新到内存中。该缓存行中的内存需要在未来的某个时间点(允许其他CPU读取主存中相应的数据之前)写回(Write Back)主存。当被写回主存之后,该缓存行的状态会变成独享状态。简单来说,处于Modified状态的缓存行数据只在本CPU中有缓存,且其数据与内存中的数据不一致,数据被修改过。
Exclusive:E状态,该缓存行的数据只在本CPU的私有高速缓存中进行了缓存,而其他CPU中没有,缓存行的数据是未被修改过的(Clean),并且与主存中的数据一致。该状态下的缓存行在任何时刻被其他CPU读取之后,其状态将变成共享状态。在本CPU修改了缓存行中的数据后,该缓存行的状态可以变成Modified状态。
Shared:S状态,该缓存行的数据可能在本CPU以及其他CPU的私有高速缓存中进行了缓存,并且各CPU私有高速缓存中的数据与主存数据一致(Clean),当有一个CPU修改该缓存行时,其他CPU中该缓存行将被作废,变成无效状态。
Invalid,I状态,该缓存行是无效的,可能有其他CPU修改了该缓存行,或者缓存行对应的变量没有被任何CPU缓存,或者本地CPU没有访问国该变量。
- PR/W是针对本地CPU,BR/W针对是其它CPU,四个状态表示的是同一个CPU上的同一个CACHE LINE的状态变化。
- 状态描述的是本地“P”上的Cache Line的状态
- 如果只有一个核(也就是只有P),则MESI状态只有 E(xclusive)和M(odified).两个之间交换。不会进入Invalid和Shared。也就是说,Invalid和Shared是多核需要保证Cache一致性的情况下才会有的状态。
- MESI协议中,E和M状态转换是单向的,E状态下执行本地写(PW)后会转换为M状态,并且M状态不能直接转换为E状态。它们能合并为一个状态么?答案是不能,原因如下:
- E(独占)状态,表示数据在cache line中,不与任何其它处理器共享。其次,E状态表明缓存行是干净的,意味着从内存中读取进CACHE LINE后,数据没有修改过。处理器对缓存行有独占访问权,在缓存行被写回内存之前,没有其他处理器可以访问它。E状态用于优化读操作。当处理器读取处于E状态的缓存行时,它可以立即返回数据,而无需与其他处理器进行检查,并且本地读(PR)不会改变E状态。
- M(修改)状态:缓存线存在于缓存中,并已被处理器修改。缓存行是脏的,这意味着它从内存中取出后已经被修改过。处理器对缓存行有独占访问权,在缓存行被写回内存之前,没有其他处理器可以访问它。M状态用于优化写操作。当处理器在M状态下写入缓存行时,它可以立即更新缓存行,而无需与其他处理器进行检查,本地写(PW)和本地读不会改变M的状态。
- E和M状态之间的主要区别是:
- 脏位:在E状态下,cacheline是干净的;在M状态下,cacheline是脏的。
- 修改:在E状态下,cacheline没有被修改(相对于内存数据);在M状态下,cacheline被修改(相对于内存数据)。
- 如果要将E和M状态合并为一个状态,将失去区分干净和脏cacheline的能力。如果没有脏位,我们就不知道缓存行是否被修改过,这可能导致处理器之间的数据不一致,产生数据一致性问题。同时,如果把E状态当成M状态,需要将干净的缓存行写回内存,就会导致不必要的写,从而降低性能。如果把M状态当成E状态,将使保持缓存一致性变得困难,因为将无法跟踪哪些cacheline被修改并需要写回内存。
- 所以,MESI协议中的E和M状态服务于不同的目的,合并为一个状态将损害缓存一致性协议的完整性和性能。
- 当cacheline状态为share,说明这个cacheline被多于1个的处理器共享,并且,处于share状态的cacheline中的数据和内存中的数据是一致的。
- 多处理器下,share状态和invalid可以在不同的处理器上共存,modifiy状态和invalid状态能够共存,exclusive状态也和invalid状态共存。可以多个处理器针对同一个数据的cacheline share或者invalid,但是不允许多个处理器针对同一个数据是M或E状态。M和E状态只能和invalid共存,不能和其他状态(S)共存。
- 当数据cacheline存在处理器处于share状态时,在一个INVALID的处理器上执行本地写,则会写穿到MEM,本地变为E状态,其他处理器变为INVALID。而如果数据cacheline在其他处理器上为INVALID状态时,本地写会直接设置CACHE LINE为M状态,数据并不会回刷到内存。
不同CPU之间针对同一个变量的CACHE LINE的状态的相容关系如下图所示,矩阵关于左上-右下对角线对称:
相容表:
状态可达表如下图所示:
其中M->E不可达,必须经过其它两个状态,这是因为处于 M 态的 Cache 行只在三种情况下回写,一种是自身被替换,此时状态转换为I态(相对于原来的变量来说);第二种是其它处理器需要写该存储块(对应BW消息),此时状态转换为I态;最后一种是其它处理器请求读该Cache行(对应BR消息),此时Cache行状态转换为S。无论上述哪种情况发生,当前Cache行都不可能成为系统中没有被修改的唯一副本。
S->M不可达,S状态外部一致性总线上和本地处理器上的任意P/B, R/W, S/~S组合,目标都是其他的三个状态。
状态可达表:
在发生CACHE Line替换的时候,可能会出现某个CACHE LINE中的数据其它CPU没有,但是此CACHE LINE却是Share状态:
1.CPU0读取a0,CPU1读取a0,CPU2读取a0,a0 cacheline处于S状态:
2.CPU0读取a2,CPU1读取a2,此时的状态即是CPU0和CPU1 share a2,但是只有CPU2 有a0, cacheline状态却是S。此时若有其他CUP读a0,则这个CPU和cup2 share a0.
不同状态的确切含义:
MESI的四种状态更确切的说是编码了有效,脏和共享这三种组合,比如S状态表示数据和其他高速缓存共享,只有干净的数据才能被多个高速缓存共享。
MESI协议实现
CPU中实现MESI协议的硬件叫做Snooper,Snooper会对 Cache和CPU之间的互联总线进行“Snoop" (i.e. watches continually).操作:
如果存在多级CACHE,则只需要在最靠近CPU的lowest Level cache实现MESI协议即可。
比如支持L3/L2/L1两级CACHE的 CPU系统,MESI协议实现在L1 CACHE上。
参考文档
https://zhuanlan.zhihu.com/p/696813060
computer science - What cache coherence solution do modern x86 CPUs use? - Stack Overflow
https://www.cs.cmu.edu/afs/cs/academic/class/15418-s19/www/lectures/12_snoopimpl.pdf
https://www.cs.utexas.edu/~pingali/CS377P/2021sp/lectures/sharedMemoryLectures/mesi.pdf
更多的CPU 体系架构动画参考:
VivioJS ALL cache coherency protocols
并发编程:缓存一致性(MESI)协议和storebuffer以及内存屏障详解_store buffer-优快云博客
https://zhuanlan.zhihu.com/p/652639218
面试系列之-MSI协议/MESI协议(JAVA基础)-腾讯云开发者社区-腾讯云