【问题标题】:Memory barrier vs Interlocked impact on memory caches coherency timing内存屏障与互锁对内存缓存一致性时序的影响
【发布时间】:2014-09-03 19:13:38
【问题描述】:

简化问题:

与内存屏障相比,由互锁操作引起的内存缓存一致性(或“刷新”)的时序是否存在差异?让我们在 C# 中考虑 - 任何互锁操作与 Thread.MemoryBarrier()。我相信是有区别的。

背景:

我阅读了很少的关于内存屏障的信息 - 对防止特定类型的内存交互指令重新排序的所有影响,但我找不到关于它们是否应该导致 立即 刷新读取的一致信息/写队列。

我实际上发现很少有消息来源提到不能保证操作的即时性(只保证防止特定的重新排序)。 例如

Wikipedia: “但是,需要明确的是,这并不意味着任何操作都会在障碍完成时完成;只有操作完成的顺序(当它们完成时)才能得到保证”

Freebsd.org(屏障是特定于硬件的,所以我猜特定的操作系统并不重要):“内存屏障只是确定内存操作的相对顺序;它们不保证内存操作的时间”

另一方面,互锁操作 - 根据它们的定义 - 导致立即刷新所有内存缓冲区以保证变量的最新值已更新 导致内存子系统使用该值锁定整个缓存行, 以防止来自任何其他 CPU/内核的访问(包括读取),直到操作完成。

我是对还是错?

免责声明:

这是我最初的问题的演变Variable freshness guarantee in .NET (volatile vs. volatile read)

EDIT1: 修正了我关于联锁操作的声明 - 内联文本。

EDIT2: 完全删除演示代码+它的讨论(因为有些人抱怨信息太多)

【问题讨论】:

  • “另一方面,互锁操作——根据它们的定义——会导致立即刷新所有内存缓冲区以保证变量的最新值已更新”——哪个定义?据我所知,唯一的保证是操作是原子的。
  • @dcastro 这是一个公平的观点!我关于 Interlocked 的陈述不正确 - 我编辑了我的问题并试图解决它。基本上,互锁操作需要对整个缓存行进行独占访问(有效地防止任何可能的过时读取),但是 - 据我所知 - 对于(任何类型的)内存屏障或易失性变量来说,情况并非如此。
  • 信息太多了。如果您想要一个好的答案,请使用:stackoverflow.com/questions/how-to-ask
  • 这是一个公平的观点,因为文档没有说明任何内容(对于这样的事情真的是一个很大的遗漏)。 win32 等价物确实 - 不必要地 - 创建完整的内存屏障,而不是更合理的获取/释放语义..
  • @NebulaeGuy 我完全删除了代码示例 + 讨论。请让我知道这是否足以恢复反对票(如果是你的)。请记住,这个主题非常复杂 - 所以它需要一些参考资料。谢谢

标签: c# multithreading memory-barriers interlocked


【解决方案1】:

要了解 C# 互锁操作,您需要了解 Win32 互锁操作。

“纯”互锁操作本身只影响操作直接引用的数据的新鲜度。

但在 Win32 中,互锁操作用于暗示完全的内存屏障。我相信这主要是为了避免在新硬件上破坏旧程序。所以 InterlockedAdd 做了两件事:interlocked add(非常便宜,不影响缓存)和 full memory barrier(相当繁重的操作)。

后来,微软意识到这很昂贵,并添加了每个操作的版本,不存在或部分内存屏障。

所以现在(在 Win32 世界中)几乎所有东西都有四个版本:例如InterlockedAdd(全围栏)、InterlockedAddAcquire(读围栏)、InterlockedAddRelease(写围栏)、纯InterlockedAddNoFence(无围栏)。

在 C# 世界中,只有一个版本,它与“经典”的 InterlockedAdd 相匹配 - 它也具有完整的内存围栏。

【讨论】:

  • 谢谢 - 这是一个非常有价值的信息。您是否有任何参考资料(最好是在线,但书也可以)来支持这一点?我想阅读更多关于此的内容。
  • Win32的最佳参考当然是MSDN:msdn.microsoft.com/en-us/library/windows/desktop/…
  • 谢谢!看起来只有 Itanium 处理器才支持“acquire-”和“release-”版本——这就是为什么这根本没有出现在 .NET 上的原因。基本的互锁操作(那些出现在 .NET 中的操作)基于互锁的处理器指令 - 目前总是执行完全屏障(通过锁定内存总线 + 使缓存行无效)...
  • ... 从技术上讲,将来可能会有一些解决方案只刷新变量,但所有高级同步结构(互斥量、信号量、锁等)都会被破坏——因为所有这些基于联锁操作 - 正如我在上面的回答中提到的 stackoverflow.com/questions/24726904/…
  • API 可用于所有处理器,内在依赖于 CPU 和编译器版本。我的链接是旧 VS;要查看支持哪些内在函数,请切换到当前的 VS 版本。例如。似乎有很多 release/aquire/no-fense 内在函数并受 ARM 支持:msdn.microsoft.com/en-us/library/ttk2z1ws(v=vs.140).aspx 在 Intel 上,no-fense 版本可能等于完整版。
【解决方案2】:

简答:CAS(联锁)操作已经(并且很可能会)是最快的缓存刷新程序。

背景: - CAS 操作由单个不可中断指令在硬件中支持。与线程调用内存屏障相比,内存屏障可以在放置屏障之后但在执行任何读/写之前立即交换(因此仍然满足屏障的一致性保证)。 - CAS 操作是大多数(如果不是全部)高级同步结构(互斥体、信号量、锁 - 查看它们的实现,您会发现 CAS 操作)的基础。如果它们不能保证立即的跨线程状态一致性,或者如果有其他更快的机制,它们就不会被使用

【讨论】:

    【解决方案3】:

    至少在 Intel 设备上,一堆机器码操作可以以 LOCK 前缀作为前缀,这确保了以下操作被视为原子操作,即使底层数据类型一次无法适应数据总线,对于例如,LOCK REPNE SCASB 将扫描一串字节以查找终止零,并且不会被其他线程中断。 据我所知,Memory Barrier 构造基本上是一个基于 CAS 的自旋锁,它导致线程等待满足某些条件,例如没有其他线程有任何工作要做。这显然是一个更高级别的构造,但毫无疑问,那里有一个条件检查,它可能是原子的,也可能是受 CAS 保护的,当你到达时你仍然要支付缓存行的价格内存屏障。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-06
      • 1970-01-01
      • 2014-09-11
      • 2017-08-02
      • 2020-03-18
      • 1970-01-01
      • 1970-01-01
      • 2015-04-28
      相关资源
      最近更新 更多