CPU缓存一致性

由于在写入操作之前,CPU 核心 1 需要先广播 RFO 请求获得独占权,在其它核心回应 ACK 之前,当前核心只能空等待,这对 CPU 资源是一种浪费 。因此,现代 CPU 会采用 “写缓冲区” 机制:写入指令放到写缓冲区后并发送 RFO 请求后,CPU 就可以去执行其它任务,等收到 ACK 后再将写入操作写到 Cache 上 。CPU Cache知识回顾【CPU缓存一致性】CPU 的高速缓存,通常可以分为 L1、L2、L3 这样的三层高速缓存,也称为一级缓存、二级缓存、三级缓存 。

CPU缓存一致性

文章插图
L1 高速缓存访问速度几乎和寄存器一样快,大小在几十 KB 到几百 KB 不等 。每个 CPU 核心都有一块属于自己的 L1 高速缓存 。
L2 高速缓存同样每个 CPU 核心都有,但是 L2 高速缓存位置比 L1 高速缓存距离 CPU 核心 更远,它大小比 L1 高速缓存更大,CPU 型号不同大小也就不同,通常大小在几百 KB 到几 MB 不等,访问速度则更慢 。
L3 高速缓存通常是多个 CPU 核心共用的,位置比 L2 高速缓存距离 CPU 核心 更远,大小也会更大些,通常大小在几 MB 到几十 MB 不等 。
cpu cache 结构CPU Cache 是由很多个 Cache Line 组成的,CPU Line 是 CPU 从内存读取数据的基本单位,而 CPU Line 是由各种标志(Tag)+ 数据块(Data Block)组成,你可以在下图清晰的看到:
CPU缓存一致性

文章插图
Cpu cache数据写入的两种方式多核CPU同时工作的时候,每个核心都会从内存中读取一份数据并缓存到自己的Cache中,当发生写操作的时候,有两种情况
  • 写直达:只要有数据写入,都会把数据同时写入内存和 Cache 中,这种方式简单直观,但是性能就会受限于内存的访问速度;
  • 写回:对于已经缓存在 Cache 的数据的写入,只需要更新其数据就可以,不用写入到内存,只有在需要把缓存里面的脏数据交换出去的时候,才把数据同步到内存里,这种方式在缓存命中率高的情况,性能会更好;
写直达
CPU缓存一致性

文章插图
写回写直达由于每次写操作都会把数据写回到内存,而导致影响性能,于是为了要减少数据写回内存的频率,就出现了写回的方法 。
  • 写回策略会在每个 Cache 块上增加一个 “脏(Dirty)” 标记位 ,当一个 Cache 被标记为脏时,说明它的数据与内存数据是不一致的;
  • 在写入操作时,我们只需要修改 Cache 块并将其标记为脏,而不需要写入内存;
  • 那么,什么时候才将脏数据写回内存呢?—— 就发生在 Cache 块被替换出去的时候:
写回策略能够减少写回内存的次数,性能会比写直达更高 。当然,写回策略在读取的时候,有可能不是纯粹的读取了,因为还可能会触发一次脏 Cache 块的写入 。
这里还有一个设计: 在目标内存块不在 Cache 中时,写直达策略会直接写入内存 。而写回策略会先把数据读取到 Cache 中再修改 Cache 数据,这似乎有点多余?其实还是为了减少写回内存的次数 。虽然在未命中时会增加一次读取操作,但后续重复的写入都能命中缓存 。否则,只要一直不读取数据,写回策略的每次写入操作还是需要写入内存 。
写回操作-写入逻辑
CPU缓存一致性

文章插图
写回操作-读取逻辑
CPU缓存一致性

文章插图
实现缓存一致性在单核 CPU 中,我们通过写直达策略或写回策略保持了Cache 与内存的一致性 。但是在多核 CPU 中,由于每个核心都有一份独占的 Cache,就会存在一个核心修改数据后,两个核心 Cache 不一致的问题 。
举个例子:
  • Core 1 和 Core 2 读取了同一个内存块的数据,在两个 Core 都缓存了一份内存块的副本 。此时,Cache 和内存块是一致的;
  • Core 1 执行内存写入操作:
在写直达策略中,新数据会直接写回内存,此时,Cache 和内存块一致 。但由于之前 Core 2 已经读过这块数据,所以 Core 2 缓存的数据还是旧的 。此时,Core 1 和 Core 2 不一致;
在写回策略中,新数据会延迟写回内存,此时 Cache 和内存块不一致 。不管 Core 2 之前有没有读过这块数据,Core 2 的数据都是旧的 。此时,Core 1 和 Core 2 不一致 。


推荐阅读