C++ 如何仅使用 MOV 在 x86 上实现释放和获取?

标签 c++ x86 memory-barriers memory-model stdatomic

这个问题是对此的跟进/澄清:

Does the MOV x86 instruction implement a C++11 memory_order_release atomic store?

这说明了 MOV汇编指令足以在 x86 上执行获取-释放语义。我们不需要LOCK 、围栏或 xchg等等 但是,我很难理解这是如何工作的。

英特尔文档第 3A 卷第 8 章指出:

https://software.intel.com/sites/default/files/managed/7c/f1/253668-sdm-vol-3a.pdf

In a single-processor (core) system....

  • Reads are not reordered with other reads.
  • Writes are not reordered with older reads.
  • Writes to memory are not reordered with other writes, with the following exceptions:


但这是针对单核的。多核部分似乎没有提到如何强制执行负载:

In a multiple-processor system, the following ordering principles apply:

  • Individual processors use the same ordering principles as in a single-processor system.
  • Writes by a single processor are observed in the same order by all processors.
  • Writes from an individual processor are NOT ordered with respect to the writes from other processors.
  • Memory ordering obeys causality (memory ordering respects transitive visibility).
  • Any two stores are seen in a consistent order by processors other than those performing the stores
  • Locked instructions have a total order.


那么怎么能MOV单独可以促进获取释放?

最佳答案

but this is for a single core. The multi-core section does not seem to mention how loads are enforced:



该部分的第一个要点是关键:单个处理器使用与单处理器系统相同的排序原则。 该语句的隐含部分是 ...从缓存一致的共享内存加载/存储时。 即多处理器系统不会引入重新排序的新方法,它们只是意味着可能的观察者现在包括其他内核上的代码,而不仅仅是 DMA/IO 设备。

共享内存访问的重新排序模型是单核模型,即程序顺序+存储缓冲区=基本上是acq_rel。其实比acq_rel稍微强一点,还好。

唯一发生的重新排序是本地的,在每个 CPU 内核内 .一旦 store 变得全局可见,它就会同时对所有其他内核可见,而在此之前对任何内核都不可见。 (除了通过存储转发进行存储的核心。)这就是为什么只有局部屏障足以在 SC + 存储缓冲区模型之上恢复顺序一致性。 (对于 x86,只有 mo_seq_cst 在 SC 存储之后才需要 mfence,以便在执行任何进一步加载之前排空存储缓冲区。
mfencelock ed 指令(这也是完全障碍)不必打扰其他核心,只需让这个等待)。

需要理解的一个关键点是,所有处理器都共享一个一致的内存共享 View (通过一致的缓存)。 Intel 的 SDM 第 8 章的最顶部定义了一些这样的背景:

These multiprocessing mechanisms have the following characteristics:

  • To maintain system memory coherency — When two or more processors are attempting simultaneously to access the same address in system memory, some communication mechanism or memory access protocol must be available to promote data coherency and, in some instances, to allow one processor to temporarily lock a memory location.
  • To maintain cache consistency — When one processor accesses data cached on another processor, it must not receive incorrect data. If it modifies data, all other processors that access that data must receive the modified data.
  • To allow predictable ordering of writes to memory — In some circumstances, it is important that memory writes be observed externally in precisely the same order as programmed.
  • [...]

The caching mechanism and cache consistency of Intel 64 and IA-32 processors are discussed in Chapter 11.



(CPU 使用 MESI 的某些变体;Intel 在实践中使用 MESIF,AMD 在实践中使用 MOESI。)

同一章还包括一些有助于说明/定义内存模型的试金石。您引用的部分实际上并不是内存模型的严格正式定义。但该款 8.2.3.2 加载和存储都不会使用类似操作重新排序 显示负载不随负载重新排序。另一部分也显示LoadStore reordering是禁止的。 Acq_rel 基本上阻止了除 StoreLoad 之外的所有重新排序,这就是 x86 所做的。 ( https://preshing.com/20120913/acquire-and-release-semantics/https://preshing.com/20120930/weak-vs-strong-memory-models/ )

有关的:
  • x86 mfence and C++ memory barrier - 询问为什么 acq_rel 不需要障碍,而是从不同的角度(想知道数据如何对其他内核可见)。
  • How do memory_order_seq_cst and memory_order_acq_rel differ? (seq_cst 需要刷新存储缓冲区)。
  • C11 Atomic Acquire/Release and x86_64 lack of load/store coherence?
  • Globally Invisible load instructions程序顺序 + 存储缓冲区与 acq_rel 并不完全相同,尤其是当您考虑仅部分重叠最近存储的负载时。
  • x86-TSO: A Rigorous and Usable Programmer’s Model for x86 Multiprocessors - x86 的正式内存模型。


  • 其他 ISA

    一般来说,大多数较弱的内存硬件模型也只允许局部重新排序,因此障碍仍然只在 CPU 内核中是局部的,只是使内核(的某些部分)等到某个条件。 (例如,x86 mfence 阻止稍后的加载和存储执行,直到存储缓冲区耗尽。其他 ISA 也受益于轻量级屏障,以提高 x86 在每次内存操作之间强制执行的内容的效率,例如阻止 LoadLoad 和 LoadStore 重新排序。https://preshing.com/20120930/weak-vs-strong-memory-models/)

    一些 ISA(现在只有 PowerPC)允许存储在对所有人可见之前对其他一些核心可见,allowing IRIW reordering .请注意 mo_acq_rel在 C++ 中允许 IRIW 重新排序;只有seq_cst禁止它。大多数 HW 内存模型比 ISO C++ 稍强,因此无法实现,因此所有内核都同意全局存储顺序。

    关于C++ 如何仅使用 MOV 在 x86 上实现释放和获取?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60314179/

    相关文章:

    c++ - 令人困惑的继承和模板

    c++ - 在嵌入式 x86 程序集中使用数组?

    c# - 锁语句的内存屏障

    具有内存排序的 C++ 原子增量

    c++ - 查找存储在二叉搜索树的所有非叶子中的数据总和? (返回整数的独立递归函数。)

    c++ - OpenCV 中的 3D 图像梯度

    c++ - 为什么不能将字符串文字连接到 __FUNCTION__?

    linux - 汇编函数整型局部变量

    x86 - 返回堆栈缓冲区?

    c# - 我不了解 volatile 和 Memory-Barrier 的是