C# volatile 和 interlocked 如何影响 cpu 缓存

C# How volatile and interlocked affect cpu cache

我正在尝试了解 C# 中的易失性 reads/writes 和互锁操作如何影响处理器缓存。

  1. 我在某些地方读到这两个操作都会刷新处理器缓存。我想知道是不是真的?

  2. 如果他们刷新缓存,他们是怎么做的。比如使用了哪些汇编指令?

  3. 如果他们刷新缓存,是否会刷新执行 cpu 的整个缓存?或者只是缓存行?

  4. 如果他们刷新缓存,将如何影响其他处理器的缓存?它们是否使其他 cpu 缓存无效,以便在其他处理器中执行的代码通过非易失性读取获得更新的值?

我在网上找不到太多关于此的信息。我对汇编语言非常擅长,所以我无法自己弄清楚。感谢对此的任何想法。

缓存是连贯的,不需要刷新其他内核就可以在我们 运行 多个线程跨 的所有 CPU 内核上看到您的商店](参见 this). https://software.rajivprab.com/2018/04/29/myths-programmers-believe-about-cpu-caches/ 也相当不错。因此原子操作没有理由刷新它们操作的缓存行。


1.是 false。 互锁操作将存储缓冲区 刷新到 缓存,并且是内存排序的完整屏障,但之后不会从缓存中逐出该行。重复 lock add [mem], 1 (x86 InterlockedAdd) 可以命中缓存。

(volatile 存储是相似的,但不是 RMW。volatile 负载对缓存没有影响,超出了普通负载的影响。我不知道 C# volatile 存储刷新存储缓冲区,或者如果它们只是 release(不是 seq_cst)排序。这个答案的其余大部分适用于易失性和互锁,但我在想只是原子 RMW当我写的时候。)

联锁操作后,该行将在该核心缓存中处于 MESI 修改状态。 (尽管如果在该原子 RMW 保持它由该内核拥有时来自另一个内核的线路请求到达,它可能会在 RMW 完成时将 MESI 状态更改为共享或无效,但这将是由于来自另一个内核的请求正在处理的核心,而不是由于互锁操作本身。)这意味着它将在所有 other 核心的私有缓存中无效。

(Microsoft 的“Interlocked*”函数只是带有 lock 前缀的指令的 AFAIK(在 x86 上)包装器,因此您阅读的有关它们的所有内容都适用,并且阻止编译时围绕操作重新排序,即使它内联。如果 C# 互锁操作与同名的 Windows C 函数不完全相同,则此答案可能需要完全正确,但我希望它们对相同的命名函数遵循相同的设计,因为具有 seq_cst 排序的原子 RMW 是一个非常标准的暴露。)

请参阅 https://preshing.com/20120930/weak-vs-strong-memory-models/ - x86 具有“强”内存模型,弱序机器上的互锁函数是原子 RMW + 完全屏障,就像在 x86 上一样。 (但它需要多条指令才能实现,屏障与原子 RMW 分开。)像 C++ memory_order_seq_cst 操作。

有关存储缓冲区的更多信息以及 CPU 需要它们的原因,请参阅 . Store buffers naturally create StoreLoad reordering(x86 允许的唯一类型,也是最昂贵的阻止类型。)