【问题标题】:Are cache operations atomic?缓存操作是原子的吗?
【发布时间】:2018-08-05 01:16:48
【问题描述】:

我正在学习 CPU 缓存,但现在我仍然对缓存一致性协议 (MESI) 有误解。想象一下,我们有 2 个内核有一个处于共享状态的缓存线。其中一个执行读取,另一个执行写入:

 ;mem is cached in Shared state
 Thread 1 (core 1)          Thread 2 (core 2)
  mov rax, [mem]            mov [mem], dword 1

核心 1 能否观察到一些中间状态。我的意思是这样的:

  1. Core 2L1D 中的缓存行标记为已修改,并将更改写入其中。
  2. core 1L1D 缓存中的缓存行仍处于Shared 状态,因此读取时恰好读取了陈旧值。
  3. 读取过时值后,core 1L1D 缓存中的行已标记为无效。

英特尔的 MESI/MESIF 实施中是否可能出现这种情况?

【问题讨论】:

  • 这并不重要,因为没有指令的全局排序,所以你不知道 thread1 是在 thread2 之前还是之后读取值。不过根据Peter's answer 的说法,thread2 在收到其 RFO 请求的回复之前不会执行写入操作,所以不,这不应该发生。
  • 我们是否假设[mem] 驻留在单个缓存行中?它可以很容易地一分为二,得到不同的结果。
  • @BoPersson 绝对是的。其实我没想过错位...

标签: assembly intel cpu-architecture cpu-cache mesi


【解决方案1】:

在第 1 步,之前核心 2 可以将该行标记为已修改,它必须通知核心 1。因此在第 2 步,该行不再位于核心 1 L1D 中。所以在第2步,在访问线路之前,核心1必须从核心2获取更新的值。

【讨论】:

    【解决方案2】:

    核心 1 的 L1D 缓存中的缓存线仍处于共享状态

    这是违反 MESI 的场景部分。在核心 2 发送的 RFO 完成之前,存储无法提交,因此核心 1 的行处于无效状态。

    不过,在您的示例中,它实际上并不是一个“中间”步骤。如果没有同步,则无法将您的不可能场景与仅在线路无效之前发生核心 1 的负载区分开来。即核心 1 的加载可以在全局顺序中出现在核心 2 的存储之前。

    在本地执行之后,存储才会成为全局可见的(它们必须退出,然后存储队列可以将它们提交到 L1D),并且 x86 的内存模型允许 StoreLoad 重新排序,因此可以延迟存储(隐藏在私有存储队列)直到稍后由核心 2 加载后变为全局可见。 (请参阅 Jeff Preshing 的 Memory Barriers Are Like Source Control Operations,了解有关内存重新排序的更多背景信息以及 StoreLoad 重新排序的含义)。


    在 MESI(以及 MESIF 或 MOESI 等所有变体)中,如果一个缓存有 E 或 M 状态的行,则其他缓存不能有该行的副本the MESI wikipedia article 中的状态表非常清楚地说明了这一点:如果一个缓存具有 E 或 M 状态,则其他缓存都是无效的。

    两个缓存永远不可能同时拥有具有不同数据的行的有效副本。这就是缓存一致的含义,阻止这种情况发生是 MESI 协议的重点。

    如果一个核心想要修改一个高速缓存行,它会获得该行的独占所有权,因此其他核心无法观察到陈旧的值。这必须发生在 商店可以提交到 L1D 之前。存储队列的存在是为了隐藏 Read-For-Ownership 的延迟(除其他外),但存储队列中的数据尚未提交到 L1D。 (相关:What happens when different CPU cores write to the same RAM address without synchronization? 有更多关于存储队列的信息)。


    顺便说一句,让我们假设[mem] 是自然对齐的,因此对其进行加载/存储是原子的(由 x86 架构保证)。 Why is integer assignment on a naturally aligned variable atomic on x86?.


    多级缓存和修改行

    使用多级缓存,脏缓存行可以向上传播。所以一条线可以在同一核心的L1D和L2中处于Modified状态。这很好,因为来自 L1D 的回写会通过 L2。

    据我了解,英特尔 CPU 中的共享包容性 L3 高速缓存不必回写到 DRAM,就可以将高速缓存行的副本共享给多个内核。因此,就普通/简单的 MESI 而言,将 L3 视为后备存储,而不是 DRAM。

    在多插槽系统上进行这项工作很棘手;我不确定是否设置好了,因此套接字中的 L3 只能缓存与连接到该套接字的 DRAM 相对应的物理地址。在任何情况下,监听请求都会在 L3 缓存未命中时在套接字之间发送,并且您可以配置许多复杂的设置来在 Xeon 系统上进行调整。 (例如,请参阅an Anandtech article about Haswell Xeon。)

    【讨论】:

    • 我有一个与单个缓存行相关的更细粒度的问题。单个缓存行是否自动更新?
    • @aku:请参阅Why is integer assignment on a naturally aligned variable atomic on x86?,了解 Intel 和 AMD 的缓存存储原子性规则。在 Intel 上,任何 1、2、4 或 8 字节的单个缓存存储如果不跨越缓存行边界,则保证是原子的。在 AMD 上,可能只有在它不跨越 8 字节边界的情况下。但 AMD 也可能在某些 CPU 上自动更新缓存,并且只会因超传输而撕裂。一些 CPU 将 SIMD 向量分成两半,例如Zen 1 将 AVX 分为两个 128 位半,但每个存储 2 个微指令,每个可能是原子的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-10
    相关资源
    最近更新 更多