multithreading - 英特尔 SFENCE 有发布语义吗?

标签 multithreading concurrency memory-barriers

似乎获得和释放语义的公认定义是这样的:
(引自 http://msdn.microsoft.com/en-us/library/windows/hardware/ff540496(v=vs.85).aspx)

An operation has acquire semantics if other processors will always see its effect before any subsequent operation's effect. An operation has release semantics if other processors will see every preceding operation's effect before the effect of the operation itself.



我已经简要地阅读了关于半内存屏障的存在,据推测,它们具有遵循上述相同语义的获取屏障和释放屏障的风格。

查找硬件指令的真实示例,我遇到了 SFENCE。这个博客( http://peeterjoot.wordpress.com/2009/12/04/intel-memory-ordering-fence-instructions-and-atomic-operations/ )说它是一种释放栅栏/屏障的形式:

Intel provides a bidirectional fence instruction MFENCE, an acquire fence LFENCE, and a release fence SFENCE.



但是阅读 SFENCE 的定义,它似乎没有提供发布语义,因为它根本不与负载同步?而据我所知,释放语义定义了所有内存操作(加载和存储)的排序。

最佳答案

LFENCE 没有获取语义; SFENCE 没有发布语义。有一个很好的理由:拥有一个具有获取语义或释放语义的独立栅栏指令,结果几乎完全没有用。对于获取/释放有任何好处,它必须与内存操作相关联。

例如,考虑在两个线程之间发送数据的常见习惯用法:

  • 处理器 A 写入缓冲区。
  • 处理器 A 将“真”写入标志。
  • 处理器 B 一直等到标志为真。
  • 处理器 B 读取缓冲区。

  • 请注意,处理器 A 必须确保在写入缓冲区后可以看到它对标志的写入。现在假设我们有一个“RFENCE”指令,它是一个释放栅栏。如果我们将指令紧跟在步骤 (1) 之后,则没有任何好处,因为步骤 2 中的写入似乎允许在 RFENCE 上向上迁移并在步骤 1 上向上迁移。

    一个类似的论点表明,执行获取的“AFENCE”指令对于确保步骤 3 中的标志读取不会在步骤 4 中向下迁移同样无用。

    Itanium 通过提供 write-with-release 和 load-with-acquire 指令,将栅栏与内存操作联系起来,优雅地解决了这个问题。

    回到 IA-32 和 Intel64:程序不使用“非临时”指令,那么其余指令的行为就好像每个加载都执行“获取”而每个存储执行“释放”。参见 Intel® 64 and IA-32 Architectures Developer's Manual: Vol. 3A 的第 8.2.3 节(和小节) .如果涉及“非临时”商店,您有几种方法来强制执行围栏:
  • 使用 SFENCE
  • 使用 MFENCE - 有点矫枉过正
  • 使用 LOCK 前缀指令(例如“LOCK INC”)写入标志。以 LOCK 为前缀的指令隐含地具有 MFENCE。
  • 使用 XCHG 来写入标志,就像它具有隐式 LOCK 前缀一样。

  • 例如,如果在较早的习惯用法中,缓冲区是使用非临时存储写入的,则让处理器 A 在步骤 1 和 2 之间发出 SFENCE 或 MFENCE。或者使用 XCHG 写入标志。

    以上所有说明均适用于硬件。使用高级语言时,请确保编译器不会破坏事件的关键顺序。存在 C++11 原子操作库,以便您可以告诉编译器和硬件您想要什么。

    关于multithreading - 英特尔 SFENCE 有发布语义吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16071682/

    相关文章:

    multithreading - 在 IIS 下增加 .NET Remoting 应用程序的并发请求

    java - 为什么 LinkedBlockingQueue 的 put() 中有一个 while 循环

    java - PriorityBlockingQueue 不阻塞?

    C++ 频繁暂停和重新启动线程的最佳方法是什么?

    vulkan - Vulkan 计算着色器缓存和屏障

    java - 调用LockSupport.parkNanos(long)后是否需要检查线程中断状态?

    .net - Spring.net WCF 服务和多线程 : manage count thread for exec WCF method

    java - swing 中的线程并不总是运行

    java - 了解执行器服务中的线程池大小

    assembly - 优化 x86 CPU 上的隔离内存存储