x86:这里需要内存屏障吗?

x86: Are memory barriers needed here?

在 WB 内存中,a = b = 0

P1:
a = 1
SFENCE
b = 1

P2:
WHILE (b == 0) {}
LFENCE
ASSERT (a == 0)

据我了解,这里不需要 SFENCELFENCE

也就是说,因为对于这种内存类型,x86 确保:

  1. 无法使用较旧的读取重新排序读取
  2. 商店不能用旧商店重新订购
  3. 商店是可传递可见的

lfencesfence asm 指令是无操作的,除非您使用 NT 存储(或从 WC 内存(例如视频 RAM)加载 NT)。 (实际上,movntdqa loads might only be ordered by mfence on paper,而不是 lfence。在那种情况下,我不知道你什么时候会使用 lfence。它与 sfence 一起添加到 ISA + mfence 与 NT 存储同时,在 movntdqa 之前,可能只是为了完整性/以防万一需要它。)

关于这一点有时会有混淆,因为 lfencesfence 的 C/C++ 内在函数也是编译器障碍。在 C/C++ 中 所需要的,但是可以使用 GNU C asm("":::"memory"); 或(为了放松-atomic 操作1) std::atomic_signal_fence(std::memory_order_acq_rel)。限制 compile-time reordering 而不让编译器发出任何无用的 asm 屏障指令。


运行-时间重新排序已经被 x86 内存模型阻止,除了 StoreLoad reordering which requires mfence to block. lfence + sfence don't add up to mfence. See Does it make any sense instruction LFENCE in processors x86/x86_64? 和关于这些指令的各种其他 SO 问答。

这就是为什么 std::atomic_thread_fence(std::memory_order_acq_rel) also compiles to zero instructions on x86,但对弱序架构的障碍。


lfence 也是 Intel 微架构上的序列化指令(但可能不是 AMD?)。一直以来都是如此,但英特尔最近正式做出了这一保证,因此 Spectre 缓解技术可以安全地使用它,而不是更不方便的 cpuid.


  • 脚注 1:
gcc 上的

atomic_signal_fence 也可能是普通非 atomic 变量的编译器障碍;这是我最后一次检查 gcc(而 atomic_thread_fence 不是),但这可能只是一个实现细节,因为没有涉及任何 atomic 变量。当有 atomic 个变量时,编译器知道这些变量可能会提供排序,让其他线程在没有 UB 的情况下访问非原子变量,因此需要排序。