15

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

如果其他处理器总是在任何后续操作的效果之前看到它的效果,则该操作具有获取语义。如果其他处理器将在操作本身的效果之前看到每个先前操作的效果,则该操作具有释放语义。

我已经简要了解了半内存屏障的存在,并且据说它们具有遵循上述相同语义的获取屏障和释放屏障的味道。

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

Intel 提供双向栅栏指令 MFENCE、获取栅栏 LFENCE 和释放栅栏 SFENCE。

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

4

1 回答 1

20

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

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

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

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

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

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

回到 IA-32 和 Intel64:如果程序不使用“非临时”指令,那么其余指令的行为就好像每次加载都执行“获取”并且每个存储执行“释放”一样。请参阅英特尔® 64 和 IA-32 架构开发人员手册:第 8.2.3 节(和小节) 。3A。如果涉及“非临时”存储,您有几种方法可以强制执行围栏:

  • 使用 SFENCE
  • 使用 MFENCE - 有点矫枉过正
  • 使用 LOCK 前缀指令(例如“LOCK INC”)写入标志。带有 LOCK 前缀的指令隐含地具有 MFENCE。
  • 使用 XCHG 来写入标志,它就像它具有隐式 LOCK 前缀一样。

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

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

于 2013-04-18T22:12:20.157 回答