MOESI一致性协议下,CPU1加载时CPU0存储缓冲区是否会刷新?
问题描述
考虑以下代码:
std::atomic<int> a = 100; --- CPU 0: a.store(101, std::memory_order_relaxed); --- CPU 1: int tmp = a.load(std::memory_order_relaxed); // 假设`tmp`为101。
假设CPU0对a的存储操作在时间上早于CPU1对a的加载操作,此时tmp的值为101。
若采用MOESI一致性协议,CPU0存储a时会以修改(M)模式获取缓存行,该操作进入CPU0的存储缓冲区;若CPU1缓存中存在该行,其副本会转为无效(I)模式。
当CPU1加载a时,缓存行会转为共享(S)模式或拥有(O)模式。
假设CPU1加载a时,a仍在CPU0的存储缓冲区中。 由于CPU1无法读取CPU0的存储缓冲区,那么当CPU1读取a所在缓存行时,是否意味着CPU0的存储缓冲区(至少包含a的缓存行)被刷新?
若未发生刷新,则会出现CPU0与CPU1同时以共享(S)模式持有该缓存行,但CPU0看到a的值为101、CPU1看到a的值为100的不一致情况。
注:本文针对MOESI协议提问,尽管各微架构实现的一致性协议不同,但此类问题的处理逻辑大致相似。
解答
是的,CPU1读取a所在缓存行时,会触发CPU0存储缓冲区中对应缓存行的刷新,具体逻辑如下:
- 总线事务触发同步:当CPU1发起读请求时,会在系统总线上广播该请求。CPU0的缓存控制器会监测到这个请求——因为它已将目标缓存行标记为M(修改)模式(即使存储操作还在缓冲区中,缓存行的权限状态已提前更新为M)。
- 缓冲区冲刷与数据提交:CPU0必须响应这个读请求,首先将存储缓冲区中针对该缓存行的未完成存储操作冲刷到缓存中,然后把更新后的缓存行数据通过总线发送给CPU1。同时,CPU0的缓存行权限会从M转为O(拥有)或S(共享),CPU1的缓存行则被设置为S模式。
- 一致性保障:这种机制是MOESI协议维护缓存一致性的核心,避免了你提到的“双CPU持S模式但数据不一致”的情况——缓存一致性协议的基本要求就是所有CPU看到的内存状态最终一致,且外部读请求必须获取到最新的已提交值。
另外需要说明:std::memory_order_relaxed仅保证操作的原子性,不强制内存顺序,但缓存一致性协议本身会确保当一个存储操作对其他CPU可见时,必然是已提交的最新值——这里的可见性触发点就是CPU1的读请求引发的总线事务,强制CPU0完成未提交的存储。
内容的提问来源于stack exchange,提问作者Jack Humphries
相关产品推荐
相关产品推荐

