首页 > 代码库 > Cache 一致性

Cache 一致性

    上一篇文章中讲到了 MESI,那么来让我们了解下,CPU 是怎么保证 Cache 一致性的。
    其实 MESI 协议不止是包括这些状态,它还包括一些处理器之间的消息来完成的,如果多个 CPU 在一个共享总线上。那么
他们可以通过处理器间消息来发送信息。因为当一个 CPU 的 cache 被修改的时候,如果此时该 cache line 处于 shared 状态,
也就意味着,其它 CPU 的 cache 中有相同的 cache line ,那么,它就应该发一个消息告诉其它 CPU 来知道修改这件事情,最
简单的办法就是它其它 CPU 的这个 Cache line 失效,这就是这些消息的用下,一般这些消息为:
    Read: 该消息会包含将在读的 Cache line 的物理地址,用于得到其它 cache 中 cache line 的内容;
    Read response: 作为 read 消息的回应,会被其它 CPU 返回,并且包含想要读取的数据;
    Invalidate: 发送该消息使其它 Cache 中指定的 Cache line 失效,它会包含相应 Cache line 的物理地址,其它 CPU 将
移除相应的 Cache line;
    Invalidate Acknowledge: 作为 Invalidate 的回应,将会在移除相应的 cache line 之后发回;
    Read Invalidate: 包含相关 cache line 的物理地址,是 Read 和 Invalidate 的合并功能,它要求一个 Read response 和
一组 Invalidate Acknowledge 作为回应;
    Writeback: 它也会包含相应 cache line 的物理地址,它要求相应的 cache line 写回到内存,这样就可以为其它数据腾出
位置。
    通过这些状态和这些消息,那么就可以在各个 CPU 核心之间保证了 cache 的一致性,举个例子。
    当一个 CPU 准备对某一内存进行写入的时候,发现,该内存已经在 cache line 中了,并且状态为 shared ,那么此时,它
就可以给其它 CPU 发送一个 invalidate 消息,其它 CPU 收到该消息后,移除自己 cache 中的该 cache line ,然后回应一个

invalidate acknowledge 消息,该 CPU 接收到回应之后,就可以进入安全的写入了,示例图如下:

                    CPU0                     CPU1 
                     |                        |
             (Write) |       Invalidate       |
                |    | ---------------------> |
                |    |                        |
                |    |                        |
                V    | Invalidate Acknowledeg |
               ---   | <--------------------- |
                     |                        |

    写的过程,就是从发起 Invalidate 消息开始,到其它 CPU 回应后,然后再进行写入,这样就可以安全的维护缓存的一致了,
但是如果总线繁忙,CPU 核数比较多时,这个过程也将会耗费很多 CPU 周期,速度却降下来了,如果 cache 被多个 CPU 共享,
那么这样的情况将会频繁发生,如果能够先把写入这个操作缓存起来,转而让 CPU0 执行其它的操作就好了。
    对了,搞硬件的这些家伙也想到了这一点,所以就有了 Store Buffers。有了 Store Buffers,不可以先把写入的值存起来,
先不更新到 cache 中,等到收到回应后再应用于 cache 中,这样就不用等回应,先执行其它操作了。
    让我们来看个例子:
    a = 1;
    b = a + 1;
    assert(a == 2);
    有了 Store Buffers,假设 a 被初始化为 0,上面的代码就可能这样执行:
    1. CPU0 执行 a = 1, 把 1 写入 地址 a 处,它会现 a 不在缓存中,所以它就发送一个 read invalidate 消息给其它 CPU。
    2. CPU0 把 a 的值 1 存入 Store Buffer,然后执行 b = a + 1;
    3. CPU0 接收到回应,并把新值存入自己的 cache line,然后读取 a 的值,此时从其它 CPU 传来的 a 的值依然为 0, 这时
执行 a + 1, 将得到 1;
    4. CPU0 此时把 Store Buffer 中的 a 的值更新到 Cache 中,不过已经造成了不幸的后果。
    分析这样的过程,不然得出,结果不正确是因为在读取时,CPU0 直接从 Cache 中读取了 a 的值,如果从 Store Buffer 中
读取,就不会有这样的情况了。
    对,那些家伙们也想到了这样的缺点,所以就发明了 Store forwarding,像这种,上一个指令(存储)还没执行完成,下一个
加载指令就开始访问内存的情况,叫做 Memory ordering, 即内存乱序访问。它无疑加快了 CPU 的执行流程,但也带来了一些负面
影响,那么 CPU 是如何解决这些负面影响,并且让执行效率越来越快呢?
    (待续)

Cache 一致性