对于在原子数据类型(例如std::atomic<uint8_t>)的对象上执行的存储,GCC 生成:
MOV释放存储( )情况下的指令std::memory_order_release,XCHG顺序一致存储情况下的指令( std::memory_order_seq_cst)。当目标架构为 x86_64 时。然而,当是ARM64(AArch64)时,在这两种情况下,GCC都会生成相同的指令,即STLRB. 没有生成其他指令(例如内存屏障),Clang 也会发生同样的情况。这是否意味着这条被描述为具有存储-释放语义的指令实际上也提供了顺序一致性?
例如,如果在两个内核上运行的两个线程将执行STLRB不同内存位置的存储,那么这两个存储的顺序是否唯一?这样所有其他线程都保证遵守相同的顺序吗?
我之所以问,是因为根据这个答案,使用acquire-loads ,不同的线程可能会观察到不同的release-store顺序。为了观察相同的顺序,需要顺序一致性。
现场演示: https: //godbolt.org/z/hajMKnd53
显然,顺序一致的原子操作的有效可观察行为与有效C++程序中的仅获取释放操作不同.定义在C++标准(自C++ 11以来)或此处给出.
但是,我从来没有遇到过一个算法或数据结构的真实例子,其中获取 - 释放语义不足并且需要顺序一致性.
什么是真实世界算法或数据结构的实际例子,其中需要顺序一致性并且获取 - 释放内存顺序是不够的?
注意,即使std::mutex不保证顺序一致性.
我正在查看来自 gcc 的 rmw 原子的编译器输出,并注意到一些奇怪的东西 - 在 Aarch64 上,诸如 fetch_add 之类的 rmw 操作可以在宽松的负载下部分重新排序。
在 Aarch64 上,可能会为以下代码生成 value.fetch_add(1, seq_cst)
.L1:
ldaxr x1, [x0]
add x1, x1, 1
stlxr w2, x1, [x0]
cbnz L1
Run Code Online (Sandbox Code Playgroud)
但是,在 ldaxr 之前发生的加载和存储可能会被重新排序,超过 stlxr 之后发生的加载和加载/存储(请参阅此处)。GCC 不会添加围栏来防止这种情况发生 - 这里有一小段代码演示了这一点:
void partial_reorder(std::atomic<uint64_t> loader, std::atomic<uint64_t> adder) {
loader.load(std::memory_order_relaxed); // can be reordered past the ldaxr
adder.fetch_add(1, std::memory_order_seq_cst);
loader.load(std::memory_order_relaxed); // can be reordered past the stlxr
}
Run Code Online (Sandbox Code Playgroud)
生成
partial_reorder(std::atomic<int>, std::atomic<int>):
ldr w2, [x0] @ reordered down …Run Code Online (Sandbox Code Playgroud)