【问题标题】:C# How volatile and interlocked affect cpu cacheC# volatile 和 interlocked 如何影响 cpu 缓存
【发布时间】:2020-11-21 04:19:22
【问题描述】:

我试图了解 c# 中的易失性读/写和互锁操作如何影响处理器缓存。

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

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

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

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

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

【问题讨论】:

  • 1.这是错误的。他们刷新存储缓冲区缓存。缓存是连贯的,不需要为其他内核刷新以查看您的存储。 software.rajivprab.com/2018/04/29/… 相当不错。 (Microsoft 的“Interlocked*”函数只是带有 lock 前缀的指令的 AFAIK(在 x86 上)包装器,因此您读到的关于它们的所有内容都适用,并阻止编译时围绕操作重新排序,即使它是内联的。)

标签: c# clr atomic cpu-architecture coreclr


【解决方案1】:

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


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

(volatile 存储类似,但不是 RMW。volatile 加载对缓存没有影响,超出普通加载的影响。我不知道 C# volatile 存储是否刷新存储缓冲区,或者如果他们只是release(不是seq_cst)订购。这个答案的其余大部分都适用于易失性和互锁,但我在写它时只是考虑原子RMW。)

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

(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 为何需要它们的更多信息,请参阅Can a speculatively executed CPU branch contain opcodes that access RAM?。存储缓冲区自然会创建StoreLoad reordering(x86 唯一允许的类型,也是最昂贵的阻塞类型。)

【讨论】:

  • 感谢您的详细解释。是的,这消除了这种困惑。那么是否只有将存储缓冲区刷新到缓存的联锁操作(不是易失性写入)?
  • @ThanujaDilhan:根据docs.microsoft.com/en-us/dotnet/csharp/language-reference/…volatile 保证按顺序查看易失性写入(所以release ordering)。该文档没有提到任何需要耗尽存储缓冲区的内容(例如顺序一致性),甚至说保证单个总订单。
  • IIRC,MSVC++ volatile 基本上只是 release,所以这与他们在 C# 中给出的含义一致。 (从历史上看,在 MSVC++ 中,volatile 只是没有编译时重新排序与其他访问包括非易失性访问(ISO C++ 不需要),并且在 x86 上赋予它获取/释放语义在 MSVC++ 中,因为强大的硬件内存模型。但在其他没有以这种方式扩展 C++ volatile 含义的编译器中当然不会。不过,如果 C# 的设计选择/命名受到 MSVC++ 的古怪影响,我不会感到惊讶。 )
  • 谢谢@Peter Cords。
猜你喜欢
  • 2013-01-07
  • 1970-01-01
  • 2015-11-28
  • 2011-12-13
  • 2020-11-15
  • 2011-07-25
  • 1970-01-01
  • 2012-11-04
相关资源
最近更新 更多