x86 LFENCE、SFENCE和MFENCE指令在什么情况下需要使用?

76

好的,我一直在阅读关于x86 CPU屏障(LFENCESFENCEMFENCE)的以下Stack Overflow Qs:

以及:

说实话,我仍然不确定何时需要屏障。我试图从移除完整锁定并尝试使用通过屏障更细粒度的锁来最小化延迟的角度理解。

首先,这里有两个我不明白的具体问题:

有时,CPU在进行存储操作时会写入其存储缓冲区而不是L1高速缓存。但我不明白CPU在何种情况下会这样做?
CPU2可能希望加载已写入CPU1的存储缓冲区中的值。据我了解,问题在于CPU2看不到CPU1的存储缓冲区中的新值。为什么MESI协议不能将刷新存储缓冲区作为其协议的一部分呢?
更普遍地说,请有人尝试描述整个场景,并解释何时需要LFENCE/MFENCESFENCE指令?
注:阅读这个主题的一个问题是有很多文章是“普遍”适用于多个CPU架构,而我只对Intel x86-64架构感兴趣。

7
MESI协议为什么不能将刷新存储缓冲区作为其协议的一部分?如果存储缓冲区必须与指令流有严格的顺序关系,它们就没有作用。而没有这样的排序,何时才会刷新它们?本质上,你的建议是:“为什么不将所有东西都减速到内核速度,而不是要求人们确定需要承受此惩罚的具体事物?”(请注意,这是尽力而为的直译,可能有更好的翻译方式,具体取决于上下文和所要表达的意思) - David Schwartz
在x86上,如果您使用的是除写回缓存之外的内存类型,或者使用非暂态指令,则几乎只需要使用栅栏。请参见此答案和其中引用的手册部分。 - Jester
2
没有显式同步的情况下,即使存储已经缓存在CPU1的存储缓冲区中,CPU2仍然可能看到旧值,这并没有问题。只有当CPU1使存储可见时,CPU2才“必须”看到它。 - Leeor
在英特尔论坛上有一篇相关帖子提到了MFENCE的使用:https://software.intel.com/en-us/forums/intel-moderncode-for-parallel-architectures/topic/537987 - jrh
1个回答

56
最简单的答案:您必须使用三个屏障中的一个(LFENCESFENCEMFENCE)来提供六种数据一致性之一:
  • 松散的
  • 消费的
  • 获取的
  • 释放的
  • 获取-释放的
  • 顺序的

C++11:

最初,您应该从内存访问顺序的角度考虑这个问题,这在C++11中得到了很好的记录和标准化。您应该首先阅读:http://en.cppreference.com/w/cpp/atomic/memory_order

x86/x86_64:

1. 获取-释放一致性: 其次,需要了解在x86中通过使用asm MOV访问传统RAM(默认标记为WB - 写回,并且与WT(写入缓存)或UC(不可缓存)具有相同的效果)自动提供获取-释放一致性的内存顺序 - std::memory_order_acq_rel。即,对于此内存,仅使用std::memory_order_seq_cst才有意义,仅为提供顺序一致性。也就是说,当您使用:std::memory_order_relaxedstd::memory_order_acq_rel时,std::atomic::store()(或std::atomic::load())的编译汇编代码将相同 - 仅为MOV,没有任何L/S/MFENCE注意:但您必须知道,不仅CPU而且C ++编译器可以重新排序对内存的操作,并且所有6个内存屏障始终会影响C ++编译器,而不管CPU架构如何。
然后,您必须知道如何将其从C++编译为ASM(本机机器代码)或如何在汇编语言中编写它。要提供任何一致性,排除顺序,您可以简单地编写MOV,例如MOV reg,[addr]MOV [addr],reg等。
2. 顺序一致性:但是,要提供顺序一致性,您必须使用隐式(LOCK)或显式栅栏(L / S / MFENCE),如此处所述:为什么GCC不使用LOAD(无fence)和STORE + SFENCE来实现顺序一致性?
  1. LOAD(无障碍)和 STORE + MFENCE
  2. LOAD(无障碍)和 LOCK XCHG
  3. MFENCE + LOADSTORE(无障碍)
  4. LOCK XADD(0)和 STORE(无障碍)

例如,GCC使用1,但MSVC使用2。(但你必须知道,MSVS2012有一个错误:`std::memory_order_acquire`的语义是否需要在x86 / x86_64上进行处理器指令?

接下来,您可以阅读Herb Sutter的文章,链接如下:https://onedrive.live.com/view.aspx?resid=4E86B0CF20EF15AD!24884&app=WordPdf&authkey=!AMtj_EflYn2507c

规则的例外:

对于使用MOV访问默认标记为WB-Write Back的传统RAM的访问,此规则是正确的。在每个PTE(页表项)中的Page Table中,内存被标记为每个页面(4 KB连续内存)。

但也有一些例外情况:

  1. 如果我们在页面表中将内存标记为写结合(在POSIX中使用ioremap_wc()),那么自动提供的只有获取一致性,我们必须按照以下段落中所述进行操作。

  2. 请参见我的问题答案:https://dev59.com/4mIk5IYBdhLWcg3wp_iM#27302931

  • 与其他写入的内存写入不会被重新排序,但有以下几种例外情况
    • 使用CLFLUSH指令执行的写入;
    • 使用非临时移动指令(MOVNTI、MOVNTQ、MOVNTDQ、MOVNTPS和MOVNTPD)执行的流式存储(写入);
    • 字符串操作(请参见第8.2.4.1节)。

在这两种情况下,即使您想要获取发布一致性,您也必须在两次写入相同地址之间使用额外的SFENCE,因为这里自动提供的只有获取一致性,您必须自己进行发布操作(SFENCE)。

回答你的两个问题:

有时在进行存储操作时,CPU 会将数据写入其存储缓冲区而不是 L1 缓存。但我不明白 CPU 会在什么情况下这样做?

从用户的角度来看,缓存 L1 和存储缓冲区的作用是不同的。L1 更快,但存储缓冲区更快。

  • Store-Buffer - 是一个简单的队列,只存储写操作,并且不能被重新排序 - 它是为了提高性能和隐藏对缓存的访问延迟(L1 - 1ns,L2 - 3ns,L3 - 10ns)而设计的(CPU核心认为写操作已经存储到缓存中并执行下一条指令,但与此同时,您的写操作仅保存在Store-Buffer中,稍后将保存到缓存L1/2/3中),即CPU核心无需等待写操作存储到缓存中。

  • Cache L1/2/3 - 看起来像透明的关联数组(地址-值)。它很快但不是最快的,因为x86自动使用cache coherent协议MESIF/MOESI提供获取-释放一致性。这是为了更简单地进行多线程编程,但会降低性能。(实际上,我们可以使用无需使用缓存一致性的写争用自由算法和数据结构,例如通过PCI Express)。协议MESIF/MOESI通过连接CPU内核和多处理器系统中不同CPU之间的Cores的QPI工作(ccNUMA)。

CPU2希望加载已写入CPU1存储缓冲区的值。据我所知,问题在于CPU2无法查看CPU1存储缓冲区中的新值。
是的。
为什么MESI协议不能将刷新存储缓冲区作为其协议的一部分?
MESI协议不能将刷新存储缓冲区作为其协议的一部分,因为:
- MESI/MOESI/MESIF协议与存储缓冲区无关,也不了解它。 - 在每次写操作时自动刷新存储缓冲区会降低性能,并使其无用。 - 使用某些命令手动刷新所有远程CPU核心(我们不知道存储缓冲区包含所需写入的哪个核心),会降低性能(在8个CPU x 15个核心=120个核心同时刷新存储缓冲区——这太可怕了)。
但是,在当前CPU核心上手动刷新存储缓冲区是可以的,可以通过执行SFENCE命令来完成。您可以在以下两种情况下使用SFENCE:
  • 提供带有写回缓存的RAM的顺序一致性
  • 对于“规则异常”的情况:提供使用CLFLUSH指令和非暂态SSE/AVX命令执行的带有写组合缓存的RAM的获取-释放一致性

注意:

在x86/x86_64处理器上是否需要LFENCE?这个问题并不总是清晰的:在处理器x86/x86_64上使用指令LFENCE是否有意义?

其他平台:

因此,您可以将其视为理论上的情况(在真空中的球形处理器),具有存储缓冲区和无效队列,您的链接:http://www.puppetmastertrading.com/images/hwViewForSwHackers.pdf

如何在其他平台上提供顺序一致性,不仅可以使用L/S/MFENCE和LOCK,还可以使用LL/SC: http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html


2
你应该将 LFENCESFENCEmfence 分开讨论。只有在使用 NT 存储时才需要 SFENCE。实际上,你几乎不需要 LFENCE 来进行内存排序,只需要利用其部分序列化效果,例如使用 rdtsc为什么(或者不是?)SFENCE + LFENCE 等同于 MFENCE?何时使用 _mm_sfence、_mm_lfence 和 _mm_mfence)。mfence 是唯一对于正常内存排序有用的屏障。lfencesfence 对于从松散到 seq_cst 的任何情况都没有用处。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接