c++ - 为什么 x86/x86_64 上的顺序语义通过 MOV [addr], reg + MFENCE 而不是 + SFENCE 使用?

标签 c++ multithreading assembly concurrency x86

在 Intel x86/x86_64 系统上有 3 种类型的内存屏障:lfence、sfence 和 mfence。关于它们的使用的问题。 对于顺序语义 (SC),对所有需要 SC 语义的存储单元使用 MOV [addr], reg + MFENCE 就足够了。但是,您可以编写整体代码,反之亦然:MFENCE + MOV reg, [addr]。显然感觉,如果存储到内存的数量通常少于从内存中加载的数量,那么使用写屏障的总成本就会更低。并且在此基础上,我们必须使用顺序存储到内存,进行了另一个优化 - [LOCK] XCHG,由于“MFENCE inside in XCHG”仅适用于内存中使用的缓存行,因此可能更便宜XCHG ( video where on 0:28:20 said that MFENCE more expensive that XCHG ).

http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html

C/C++11 Operation x86 implementation

  • Load Seq_Cst: MOV (from memory)
  • Store Seq Cst: (LOCK) XCHG // alternative: MOV (into memory),MFENCE

注意:有一个 C/C++11 到 x86 的替代映射,它不是锁定(或屏蔽)Seq Cst 存储,而是锁定/屏蔽 Seq Cst 加载:

  • Load Seq_Cst: LOCK XADD(0) // alternative: MFENCE,MOV (from memory)
  • Store Seq Cst: MOV (into memory)

不同之处在于,ARM 和 Power 内存屏障仅与 LLC(末级缓存)交互,而 x86 与低级缓存 L1/L2 交互。 在 x86/x86_64 中:

  • lfence 在 Core1 上:(CoreX-L1) -> (CoreX-L2) -> L3-> (Core1-L2) -> (Core1-L1)
  • sfence 在 Core1 上:(Core1-L1) -> (Core1-L2) -> L3-> (CoreX-L2) -> (CoreX-L1)

在 ARM 中:

  • ldr; dmb;: L3-> (Core1-L2) -> (Core1-L1)
  • dmb;海峡; dmb;: (Core1-L1) -> (Core1-L2) -> L3

由 GCC 4.8.2 编译的 C++11 代码 - x86_64 中的 GDB:

std::atomic<int> a;
int temp = 0;
a.store(temp, std::memory_order_seq_cst);
0x4613e8  <+0x0058>         mov    0x38(%rsp),%eax
0x4613ec  <+0x005c>         mov    %eax,0x20(%rsp)
0x4613f0  <+0x0060>         mfence

但为什么在 x86/x86_64 上通过 MOV [addr], reg + MFENCE 使用顺序语义 (SC) 而不是 MOV [addr], reg + SFENCE,为什么我们需要全围栏 MFENCE 而不是那里的 SFENCE

最佳答案

sfence不会阻止 StoreLoad 重新排序。除非有任何 NT 商店在飞行中,否则它在架构上是一个空操作。商店在它们自己提交到 L1d 并变得全局可见之前已经等待旧商店提交,因为 x86 不允许 StoreStore 重新排序。 (除了NT存储/存储到WC内存)

对于 seq_cst,您需要一个完整的屏障来刷新存储缓冲区/确保所有旧存储在任何后续加载之前都是全局可见的。参见https://preshing.com/20120515/memory-reordering-caught-in-the-act/例如未能使用 mfence在实践中会导致非顺序一致的行为,即内存重新排序。


如您所见,可以将 seq_cst 映射到 x86 asm,并在每个 seq_cst 加载而不是每个 seq_cst 存储/RMW 上使用完全屏障。在那种情况下,您不需要任何有关商店的屏障说明(因此它们具有发布语义),但您需要 mfence在每个 atomic::load(seq_cst) 之前.

关于c++ - 为什么 x86/x86_64 上的顺序语义通过 MOV [addr], reg + MFENCE 而不是 + SFENCE 使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19001823/

相关文章:

c++ - 作为参数传递后无法计算数组元素

c# - 确保字典中值的列表的线程安全操作

assembly - 我的标志标志值显示错误?我对标志旗的工作原理有误解吗?

c++ - 仅使用 volatile 修复 DCLP

c++ - 使用 Boost Qi 解析为结构的随机顺序

java - Threadpoolexecutor 不更新并发 HashMap

gcc - 使用 SSE 指令时是什么导致了此段错误?

linux - nasm array1.o : file not recognized: File format not recognized on Linux 32

c++ - C++使用三元运算符声明变量

java - 线程池中线程的运行时异常完全消失: how to handle