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 是如何解决这些负面影响,并且让执行效率越来越快呢?
    (待续)
### 缓存一致性在分布式系统中的重要性 缓存一致性是指在一个分布式环境中多个节点上的数据副本保持同步的状态。当某个节点更新了数据,其他节点应该能够及时感知到这一变化并相应调整自己的副本[^1]。 ### 实现方式 为了维持这种一致性,在分布式系统中有多种机制可以采用: - **时间戳法**:给每一个写入操作分配一个全局唯一的时间戳,任何读请求都携带当前已知的最大时间戳去获取最新的版本。 - **向量时钟(Vector Clocks)**:这是一种更复杂的因果关系追踪方法,通过记录一系列事件发生顺序来决定哪个状态是最新的。 - **Quorum协议**:规定每次读写都需要获得一定数量的认可才能成功完成,以此保证大多数副本之间的一致性[^3]。 另外,针对特定应用场景如大规模数据分析平台(例如GFS),由于处理海量数据集的特点使得传统的客户端侧缓存策略变得低效甚至有害。因此这类系统通常不会依赖于客户端缓存而是采取更为高效的设计思路[^2]。 ```python class CacheConsistencyMechanism: def __init__(self, method="timestamp"): self.method = method def apply(self): if self.method == "quorum": print("Using Quorum Protocol to ensure cache consistency.") elif self.method == "vector_clock": print("Applying Vector Clocks for causality tracking.") else: print("Implementing Timestamp-based synchronization.") mechanism = CacheConsistencyMechanism(method="quorum") mechanism.apply() ``` ### 解决方案的选择考量因素 选择合适的缓存一致性解决方案取决于具体的应用需求和技术约束条件。对于那些对延迟敏感或者网络带宽有限的情况来说,可能更适合使用较为宽松的一致性模型;而对于金融交易之类要求强一致性的业务,则应优先考虑更加严格的方法[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值