4

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

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

C/C++11 操作 x86 实现

  • 加载 Seq_Cst:MOV(从内存中)
  • Store Seq Cst: (LOCK) XCHG // 替代: MOV (入内存),MFENCE

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

  • 加载 Seq_Cst: LOCK XADD(0) // 备选方案:MFENCE,MOV(从内存中)
  • Store Seq Cst: MOV (入内存)

不同之处在于 ARM 和 Power 内存屏障仅与 LLC(Last Level Cache)交互,而 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; str; 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 Sequential Semantic (SC) 上使用 throughMOV [addr], reg + MFENCE而不是MOV [addr], reg + SFENCE,为什么我们需要 full-fenceMFENCE而不是SFENCE那里?

4

2 回答 2

2

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).

于 2019-03-19T01:25:52.770 回答
-1

你不需要mfence; sfence确实够了。事实上,lfence除非您正在处理设备,否则您永远不需要 x86。但是英特尔(我认为 AMD)有(或至少有)一个与 mfencesfence(即刷新存储缓冲区)共享的单一实现,因此使用较弱的sfence.

顺便说一句,请注意,您不必在每次写入共享变量后刷新;您只需在不同共享变量的写入和后续读取之间刷新。

于 2013-11-05T08:41:47.973 回答