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)
有人可以解释一下吗?
我刚刚了解这些东西,所以要持保留态度。但我的理解是,在 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在测试程序中遇到锁定冲突。