代码之家  ›  专栏  ›  技术社区  ›  Jack Humphries

一致性协议和存储缓冲区

  •  0
  • Jack Humphries  · 技术社区  · 2 年前

    请考虑以下代码:

    std::atomic<int> a = 100;
    ---
    CPU 0:
    a.store(101, std::memory_order_relaxed);
    ---
    CPU 1:
    int tmp = a.load(std::memory_order_relaxed);  // Assume `tmp` is 101.
    

    让我们假设CPU 0恰好存储到 a 在CPU 1加载之前的较早时间 (无论负载是否重新排序)。因此在这种情况下, tmp 将是101而不是100。

    如果使用MOESI一致性协议,则当CPU 0存储到 ,CPU 0在修改(M)模式下获取高速缓存行。存储进入CPU 0的存储缓冲区。如果CPU 1在其自己的高速缓存中具有高速缓存行,则其高速缓存行的副本转换为无效(I)模式。

    当CPU 1加载时 ,高速缓存线转换到共享(S)模式(或者可能是拥有(O)模式)。

    假设 在CPU 1加载时仍在CPU 0的存储缓冲区中 假定CPU 1无法读取CPU 0的存储缓冲区,则当CPU 1读取具有 ,这是否意味着CPU 0的存储缓冲区被刷新(或者至少是缓存行 从CPU 0的存储缓冲区中刷新)?

    如果没有发生刷新,那么这意味着CPU 0和CPU 1都有共享(S)模式下的缓存线,但CPU 0看到 值为101,CPU1看到 值为100。

    注意:当每个微体系结构实现自己的一致性协议时,我问的是MOESI。不过,我可以想象,在大多数微体系结构中,这种担忧也会得到类似的处理。

    0 回复  |  直到 2 年前
        1
  •  1
  •   Peter Cordes    2 年前

    存储缓冲区不会被来自其他核心的负载窥探;它们是私人的。当存储从存储缓冲区提交到L1d缓存时,它们将全局可见。(核心必须获得MESI对该线路的独家所有权 之前 它可以做到这一点,E或M状态。)

    这必须等到存储指令毕业后,也就是从ROB(ReOrder Buffer)中退休,所以它是非推测性的。A. store buffer is necessary to allow speculative execution of stores ,包含到该核心的推测状态,如果检测到错误推测(例如分支预测错误或早期指令中的错误),则可能需要回滚该推测状态。

    在全局可见(对任何其他核心)之前,核心可以看到自己的存储(通过存储转发)。 这种“重新排序”与存储缓冲区在以后加载到不同地址时引入的通常的StoreLoad重新排序有些不同。 另请参阅 Globally Invisible load instructions 进行一些讨论 部分 与一家商店看到的价值相重叠,这是其他核心无法看到的。)

    x86的TSO内存模型是带有存储缓冲区的程序顺序+存储转发 1. 用于每个核心对一致共享缓存的访问。(参见Preshing的类比, Memory Barriers Are Like Source Control Operations .)提到存储转发很重要,因为如果“命中”存储缓冲区中已有地址的加载在存储缓冲区提交到缓存之前刚刚停止,它可能会产生你看不到的效果。

    必须以独占方式拥有缓存行,存储才能 犯罪 到L1d(并变得全局可见),但如果没有这一点,则可以将存储转发到该核心自己的负载。

    (在大多数体系结构上,提交L1d和MESI一致性是存储在当前核心之外变得可见的唯一途径。但PowerPC允许将“分级”存储转发到其他逻辑SMT核心, making IRIW reordering possible )


    脚注1 :这就是486或P5 Pentium“自然”所做的,在x86内存模型真正被记录下来之前,它有有序的管道和存储缓冲区。P6尽量不引入任何新的内存重新排序,以避免破坏现有的多线程代码。它 推测地 提前加载,但如果检测到缓存线在实际加载和体系结构允许加载之间已无效,则会使用内存顺序错误推测管道nuke回滚。