提问者:小点点

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


在Intel x86/x86_64系统有3种类型的内存屏障:l的,栅栏的和m的。在他们的使用方面的问题。对于顺序语义(SC)足以使用MOV[addr],reg MFENCE对于所有需要SC-语义学的存储单元。然而,你可以写代码在整体上,反之亦然:MFENCE MOV reg,[addr]。显然感觉到,如果存储到内存的数量通常少于它的负载,那么使用写屏障的总成本更低。在此基础上,我们必须使用顺序存储到内存,做了另一个优化-[LOCK]XCHG,这可能是更便宜的,由于这一事实,MFENCE内XCHG适用于仅在XCHG中使用的内存的缓存行(视频在0:28:20说MFENCE更昂贵的XCHG)。

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

C/C 11操作x86实施

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

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

  • 加载Seq_Cst:LOCK XADD(0)//替代:MFENCE,MOV(从内存)
  • 存储Seq Cst: MOV(进入内存)

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

    Core1上的
  • lgrid:(CoreX-L1)-

在ARM:

  • ldr; dmb;:L3-

C 11代码编译GCC4.8.2-GDBx86_64:

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_64Sequential Semantic(SC)上使用通过MOV[addr],reg MFENCE,而不是MOV[addr],reg SFENCE,为什么我们需要全GeofenceMFENCE而不是SFENCE


共2个答案

匿名用户

sgrid不会阻止StoreLoad重新排序。除非有任何NT存储正在运行,否则在架构上是无操作的。存储已经等待旧存储提交,然后它们自己提交到L1d并变得全局可见,因为x86不允许StoreStore重新排序。(除了NT存储/存储到WC内存)

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

正如你所发现的,可以在每个seq_cst负载上映射seq_cst到x86 asm,而不是在每个seq_cststore/RMW上。在这种情况下,你不需要商店上的任何屏障说明(所以他们会发布语义学),但是你需要在每个atomic::load(seq_cst)之前使用mgrid

匿名用户

你不需要一个的栅栏的栅栏确实足够了。事实上,在x86中你永远不需要的栅栏,除非你正在处理一个设备。但是英特尔(我认为AMD)已经(或者至少已经)与的栅栏的栅栏共享一个实现(即刷新存储缓冲区),所以使用较弱的栅栏没有性能优势。

BTW,请注意,您不必在每次写入共享变量后刷新;您只需在不同共享变量的写入和后续读取之间进行刷新。