【问题标题】:When are x86 LFENCE, SFENCE and MFENCE instructions required?什么时候需要 x86 LFENCE、SFENCE 和 MFENCE 指令?
【发布时间】:2024-01-20 00:54:02
【问题描述】:

好的,我一直在阅读 SO 关于 x86 CPU 围栏的以下问题(LFENCESFENCEMFENCE):

和:

老实说,我仍然不能完全确定何时需要围栏。我试图从移除完全成熟的锁的角度来理解,并尝试通过栅栏使用更细粒度的锁定,以最大限度地减少延迟延迟。

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

有时在执行存储时,CPU 会写入其存储缓冲区而不是 L1 缓存。但是我不明白 CPU 执行此操作的条件?

CPU2 可能希望加载一个已写入 CPU1 存储缓冲区的值。据我了解,问题是 CPU2 无法在 CPU1 的存储缓冲区中看到新值。为什么 MESI 协议不能只将刷新存储缓冲区作为其协议的一部分??

更一般地说,有人可以尝试描述整体情况并帮助解释何时需要 LFENCE/MFENCESFENCE 指令吗?

NB 围绕这个主题阅读的一个问题是,当我只对 Intel x86-64 架构感兴趣时,“一般”为多个 CPU 架构编写的文章数量。

【问题讨论】:

  • "为什么 MESI 协议不能仅将刷新存储缓冲区作为其协议的一部分??" 如果存储缓冲区必须严格按照指令排序流,它们将毫无用处。没有这样的命令,你什么时候刷新它们?从本质上讲,您的建议是“我们为什么不将一切都放慢到内核间速度,而不是要求人们确定需要遭受这种惩罚的具体事物?”
  • 在 x86 上,如果您使用回写缓存以外的内存类型,或者使用非临时指令,您几乎只需要使用防护。另请参阅this answer,以及其中引用的手册部分。
  • 如果没有任何显式同步,即使存储已经在 CPU1 的存储缓冲区中缓冲,CPU2 也可能会看到旧值,这没有什么问题。仅当 CPU1 使存储可见时,CPU2“必须”看到它。
  • 英特尔论坛上有一篇相关帖子提到了MFENCE的使用:software.intel.com/en-us/forums/…

标签: multithreading assembly x86 cpu memory-fences


【解决方案1】:

最简单的答案:您必须使用 3 个栅栏之一(LFENCESFENCEMFENCE)来提供 6 个数据一致性之一:

  • 放松
  • 消费
  • 获取
  • 发布
  • 获取-发布
  • 顺序

C++11:

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

x86/x86_64:

1. Acquire-Release Consistency: 那么,重要的是要明白在x86中访问常规RAM(默认标记为WB-Write Back,和WT相同的效果(Write通过使用 asm MOV 而不使用任何其他命令自动提供获取-释放一致性的内存顺序 - std::memory_order_acq_rel。 IE。因为这个内存只使用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),如下所述:Why GCC does not use LOAD(without fence) and STORE+SFENCE for Sequential Consistency?

  1. LOAD(无围栏)和STORE + MFENCE
  2. LOAD(无围栏)和LOCK XCHG
  3. MFENCE + LOADSTORE(没有围栏)
  4. LOCK XADD ( 0 ) 和 STORE (没有围栏)

比如GCC用1,而MSVC用2。(但你要知道,MSVS2012有一个bug:Does the semantics of `std::memory_order_acquire` requires processor instructions on x86/x86_64?

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

规则例外:

此规则适用于通过使用MOV 访问默认标记为 WB - 回写的常规 RAM。内存标记在Page Table,在每个PTE(Page Table Enrty)中为每个Page(4 KB 连续内存)。

但也有一些例外:

  1. 如果我们将页表中的内存标记为写入组合(POSIX 中为ioremap_wc()),则自动只提供 Acquire Consistency,我们必须按照以下段落操作。

  2. 查看我的问题的答案:https://*.com/a/27302931/1558037

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

在两种情况 1 和 2 中,即使您想要获取-发布一致性,您也必须在两次写入同一地址之间使用额外的 SFENCE,因为这里自动提供仅获取一致性,您必须自己执行发布 (SFENCE) .

回答您的两个问题:

有时在进行存储时,CPU 会写入其存储缓冲区 而不是 L1 缓存。但是我不明白关于 哪个 CPU 会这样做?

从用户的角度来看,缓存 L1 和 Store Buffer 的作用不同。 L1 快,但 Store-Buffer 更快。

  • Store-Buffer - 是一个简单的队列,其中仅存储写入,并且不能重新排序 - 它用于提高性能和隐藏访问缓存的延迟(L1 - 1ns、L2 - 3ns、L3 - 10ns)(CPU-Core认为Write已经存入缓存并执行下一条命令,但同时你的Writes只存入Store-Buffer,稍后会存入缓存L1/2/3),即CPU - 当写入被存储到缓存时,核心不需要等待。

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

CPU2 可能希望加载一个已写入 CPU1 的值 存储缓冲区。据我了解,问题是 CPU2 看不到 CPU1 的存储缓冲区中的新值。

是的。

为什么 MESI 协议不能只是 包括刷新存储缓冲区作为其协议的一部分??

MESI 协议不能只将刷新存储缓冲区作为其协议的一部分,因为:

  • MESI/MOESI/MESIF protoclos 与 Store-Buffer 无关,也不知道。
  • 在每次写入时自动刷新存储缓冲区会降低性能 - 并使其无用。
  • 使用某些命令手动刷新所有远程 CPU 核心上的存储缓冲区(我们不知道哪个核心存储缓冲区包含所需的写入)- 会降低性能(在 8 个 CPU x 15 个核心 = 120 个核心)时间刷新存储缓冲区 - 这太糟糕了)

但是手动刷新当前 CPU-Core 上的存储缓冲区 - 是的,您可以通过执行 SFENCE 命令来完成。您可以在两种情况下使用SFENCE

  • 通过可写回缓存在 RAM 上提供顺序一致性
  • 规则的例外情况上提供获取-释放一致性:具有可缓存写入组合的 RAM,用于使用 CLFLUSH 指令执行的写入以及用于非临时 SSE/AVX 命令

注意:

在 x86/x86_64 上是否需要 LFENCE? - 问题并不总是很清楚:Does it make any sense instruction LFENCE in processors x86/x86_64?

其他平台:

然后,您可以在理论上使用 Store-Buffer 和 Invalidate-Queue 阅读(对于真空中的球形处理器),您的链接:http://www.puppetmastertrading.com/images/hwViewForSwHackers.pdf

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

【讨论】: