ARM STLR 内存排序语义

pve*_*jer 7 concurrency multithreading arm atomic arm64

我正在努力了解 ARM STLR 的确切语义。

根据文档,它具有发布语义。所以如果你有 STLR 商店,你会得到:

[StoreStore][LoadStore]
X=r1
Run Code Online (Sandbox Code Playgroud)

其中X是内存和r1一些寄存器。

问题是释放存储和获取加载无法提供顺序一致性:

[StoreStore][LoadStore]
X=r1
r2=Y
[LoadLoad][LoadStore]
Run Code Online (Sandbox Code Playgroud)

在上述情况下,允许重新排序 X=r1 和 r2=Y。为了使这个顺序一致,需要添加一个[StoreLoad]:

[StoreStore][LoadStore]
X=r1
[StoreLoad]
r2=Y
[LoadLoad][LoadStore]
Run Code Online (Sandbox Code Playgroud)

你通常在商店里这样做,因为装载更频繁。

在 X86 上,普通存储是发布存储,普通加载是获取加载。[StoreLoad] 可以通过 MFENCE 来实现,或者使用LOCK ADDL %(RSP),0Hotspot JVM 中的方式来实现。

当查看ARM文档时,LDAR似乎具有获取语义;所以这将是[LoadLoad][LoadStore]。

但 STLR 的语义很模糊。当我使用 memory_order_seq_cst 编译 C++ 原子时,只有一个 STLR;没有DMB。所以看来STLR比release store有更强的内存排序保证。对我来说,在栅栏层面上,STLR 相当于:

 [StoreStore][LoadStore]
 X=r1
 [StoreLoad]
Run Code Online (Sandbox Code Playgroud)

有人可以解释一下吗?

Nat*_*dge 6

我刚刚了解这些东西,所以要持保留态度。但我的理解是,在 ARMv8/AArch64 中,STLR/LDAR确实提供了超出释放/获取的通常定义的附加语义,但不如您的建议那么强。也就是说,发布存储确实与按程序顺序跟随在其后面的STLR获取加载具有顺序一致性,但与普通加载不具有顺序一致性。LDARLDR

来自 ARMv8 架构参考手册,B2.3.7,“Load-Acquire、Load-AcquirePC 和 Store-Release”:

当Load-Acquire按程序顺序出现在Store-Release之后时,由Store-Release指令生成的存储器访问被每个PE观察到在PE需要连贯地观察该访问之前,在由Store-Release指令生成的存储器访问之前。加载-获取指令由该PE观察,达到要求PE连贯地观察访问的程度。

并从 B2.3.2,“排序关系”:

当且仅当 RW1 在程序顺序中出现在 RW2 之前并且以下任一情况适用时,读取或写入 RW1 是在来自同一观察者的读取或写入 RW2 之前按屏障顺序排列的: [...] RW1 是写入W1是Release语义的指令生成的,RW2是Acquire语义的指令生成的读R2。

作为测试,我借用了LWimsey 的 Peterson 锁定算法的 C++ 实现。使用godbolt 上的 clang 11.0,您可以看到,即使请求顺序一致性,编译器仍然生成STLR, LDAR获取锁(程序集的第 18-19 行),而没有DMB. 我运行了一段时间(Raspberry Pi 4B、Cortex A72、4 核),没有发现任何违规情况。

但是,与您的想法相反,STLR仍然可以针对其后的普通(非获取)加载进行重新排序,因此它不会隐式具有完整的 StoreLoad 栅栏。我修改了 LWimsey 的程序来使用STLR, LDR它,并在添加一些额外的垃圾来引发竞争后,我能够看到锁定违规。

同样,LDAR可以针对其之前的普通(非发布)商店重新排序。我同样能够STR, LDAR在测试程序中遇到锁定冲突。

  • NB ARMv8.3 发明了遵循 RCpc(与处理器一致性的发布一致性)的 LDAPR 指令,与遵循 RCsc(具有顺序一致性)的 LDAR 不同。LDAR 的执行被推迟到先前的 STLR 完成为止,但这对于 LDAPR 来说不是必需的。但没有 LDAPXR :) (5认同)