MESI 缓存一致性协议
多核场景下,仅仅依靠CAS这一类硬件原语并不能实现同步,因为原子指令底层实现也可能包含多条微指令,而原子指令的原子性是相对于一个核而言的,多条原子指令在各自的CPU核上都是原子执行的。所以要解决这个问题就要先将总线锁住。例如在Go中Mutex的实现,在执行CMPXCHGL指令直线先LOCK总线。锁住总线就导致程序由并行变为串行,这必然会影响性能,现代CPU都拥有高速缓存,不再通过锁总线的方式实现多核间的同步,为了保证多核间高速缓存的一致性,引入了高速缓存一致性协议(MESI协议)。
单核CPU的cache中每个cache line有2个标志:dirty、valid标识,表示cache中的数据是否被修改、是否有效。而在多个核中每个CPU都有自己对应的cache,多个核共享内存中的数据,MESI协议就描述了数据的共享状态,分别是:
状态 | 描述 |
---|
M(Modified) | 该行数据有效、被修改,和内存中的数据不一致,该数据只存在于本Cache中。 | E(Exclusive) | 该行数据有效、和内存中的数据一致,该数据只存在于本Cache中。 | S(Shared) | 该行数据有效,和内存中的数据一致,该数据存在于多个Cache中。 | I(Invalid) | 该行数据无效。 |
M与E的共同点是:都是本Cache所独有的;不同点是:M表示Cache中的数据与内存中的数据不一致。
在MESI协议中,每个Cache的Cache控制器不仅要知道自己的读写操作,还要监听其他Cache的读写操作(监听总线)。
状态变化如下,其中Local Read/Write表示对本CPU Cache的读写操作,Remote Read/Write表示其他CPU对自己Cache的读写操作。
当前状态 | 事件 | 行为 | 下一个状态 |
---|
I(Invalid) | Local Read | 如果其它Cache没有这份数据,本Cache从内存中取数据,Cache line状态变成E; 如果其它Cache有这份数据,且状态为M,则将数据更新到内存,本Cache再从内存中取数据,2个Cache 的Cache line状态都变成S; 如果其它Cache有这份数据,且状态为S或者E,本Cache从内存中取数据,这些Cache 的Cache line状态都变成S | E/S | I(Invalid) | Local Write | 如果其它Cache没有这份数据,本Cache从内存中取数据并修改,Cache line状态变成M;如果其它Cache有这份数据,且状态为M,则要先将数据更新到内存,读取并修改,其他Cache的Cache line状态变为I; | M/I | I(Invalid) | Remote Read | 既然是Invalid,别的核的操作与它无关 | I | I(Invalid) | Remote Write | 既然是Invalid,别的核的操作与它无关 | I | E(Exclusive) | Local Read | 从Cache中取数据,状态不变 | E | E(Exclusive) | Local Write | 写入Cache,状态变为M | M | E(Exclusive) | Remote Read | 数据和其它核共用,状态变成了S | S | E(Exclusive) | Remote Write | 数据被修改,本Cache line不能再使用,状态变成I | I | S(Shared) | Local Read | 从Cache中取数据,状态不变 | S | S(Shared) | Local Write | 修改Cache中的数据,状态变成M,其它核共享的Cache line状态变成I | M | S(Shared) | Remote Read | 状态不变 | S | S(Shared) | Remote Write | 数据被修改,本Cache line不能再使用,状态变为I | I | M(Modified) | Local Read | 从Cache中取数据,状态不变 | M | M(Modified) | Local Write | 修改Cache中的数据,状态不变 | M | M(Modified) | Remote Read | 写会内存,使其它核能使用到最新的数据,状态变为S | S | M(Modified) | Remote Write | 写会内存,使其它核能使用到最新的数据,数据被修改,本Cache line不能再使用,状态变为I | I |
|