STLR(B) 是否在 ARM64 上提供顺序一致性?

问题描述

对于在原子数据类型(例如 std::atomic<uint8_t>)的对象上执行的存储,GCC 生成

  • MOV 指令在 release-store (std::memory_order_release) 的情况下,
  • XCHG 指令,用于 sequential-consistent-store (std::memory_order_seq_cst)。

当目标架构是 x86_64 时。但是,当是ARM64(AArch64)时,两种情况下,GCC都会生成相同的指令,即STLRB。没有生成其他指令(例如内存屏障),Clang 也会发生同样的情况。这是否意味着这个被描述为具有 store-relase 语义的指令实际上也提供了顺序一致性?

例如,如果运行在两个内核上的两个线程将使用 STLRB 执行存储到不同的内存位置,那么这两个存储的顺序是否唯一?保证所有其他线程都遵守相同的顺序?

我之所以这么问,是因为根据 this answer,对于 acquire-loads,不同的线程可能会观察 release-stores 的不同顺序。为了观察相同的顺序,需要顺序一致性。

现场演示:https://godbolt.org/z/hajMKnd53

解决方法

是的,stlr 本身就是 store-release,而 ldar 不能传递更早的 stlr(即没有 StoreLoad 重新排序)——它们之间的交互满足了acq / rel 没有的 seq_cst 要求。 (ARMv8.3 ldapr 就像 ldar 没有这种交互,只是简单的获取负载,允许更高效的 acq_rel。)

所以在 ARMv8.3 上,seq_cst 和 acq/rel 的区别在于负载端。 8.3 之前的 ARMv8 无法在仍然允许 StoreLoad 重新排序的同时执行 acq / rel,因此不幸的是,如果您在发布存储之后获取加载其他内容,速度会很慢。 ARMv8.3 修复了这个问题,使 acq/rel 与 x86 一样高效。

在 x86 上,一切都是获取加载或释放存储(因此 acq_rel 是免费的),实现顺序一致性的最坏方法是对 seq_cst 存储进行完全屏障。 (您希望原子负载便宜,并且代码使用默认的 seq_cst 内存顺序是很常见的。)

C/C++11 mappings to processors 讨论了想要廉价加载的权衡,如果您必须选择加载或存储来附加完整的障碍。)


另外,IRIW 试金石(所有线程都同意独立存储的顺序)由 ARMv8 内存模型保证,即使对于发布存储也是如此。它保证是“multicopy-atomic”,这意味着当一个存储对任何其他核心可见时,它同时对所有其他核心可见。这足以让所有核心就所有商店的总订单达成一致,达到他们可以通过两次获取负载观察到的任何限制。

实际上,这意味着存储只有通过提交到 L1d 缓存才变得可见,这是一致的。例如,不是通过共享物理核心 the mechanism for IRIW reordering on the few POWER CPUs that can produce the effect in real life 的逻辑核心之间的存储转发。 ARMv8 最初在纸面上允许这样做,但从来没有 ARM CPU 这样做过。他们加强了内存模型,以简单地保证未来的 CPU 不会像那样奇怪。有关详细信息,请参阅 Simplifying ARM Concurrency: Multicopy-Atomic Axiomatic and Operational Models for ARMv8

请注意,所有线程能够就订单达成一致的这种保证适用于 ARM64 上的所有存储,包括宽松的。 (在具有一致共享内存的机器中,很少有硬件机制可以创建它,因此只有在罕见的 ISA 上 seq_cst 必须实际执行任何特定操作以防止它。)

x86 的 TSO(总存储订单)内存模型在名称中具有所需的属性。是的,它更强大,基本上是程序顺序加上带有存储转发的存储缓冲区。 (所以这允许 StoreLoad 重新排序,并允许内核 see its own stores before they're globally visible,但没有别的。忽略 NT 存储,以及从 WC 内存(如视频 RAM)加载 NT...)