ARM与x86的std::atomic内存序表现不一致,根源在于ARM采用弱内存模型而x86采用强内存模型,导致相同代码在两平台上的语义、开销及指令生成均不同。

std::atomic 的内存序在 ARM 和 x86 上表现不一致,根源是硬件内存模型差异
ARM 使用弱内存模型(Weak Memory Model),x86 使用强内存模型(Strong Memory Model)。这意味着相同 C++ 代码在两种架构上,std::atomic 的默认行为和显式内存序的实际开销、语义约束都不同。不是“实现不同”,而是硬件不保证某些重排,编译器和运行时必须用额外指令补足。
x86 上 std::memory_order_relaxed 几乎无成本,ARM 上可能插入 dmb ish
x86 的 store-store、load-load、load-store 重排被硬件禁止,所以多数 std::atomic 操作(即使是 relaxed)无需内存屏障指令。ARM 则不然:即使 relaxed store,也可能被乱序到后续非原子访存之后,因此编译器常插入 dmb ish(inner shareable domain barrier)来满足 C++ 标准对“修改顺序一致性”的最低要求(尤其在多核间可见性上)。
-
std::atomic,线程 A 执行x{0}, y{0}; x.store(1, std::memory_order_relaxed); y.store(1, std::memory_order_relaxed);,线程 B 观察到y==1 && x==0在 x86 不可能,在 ARM 是可能的 —— 除非加std::memory_order_release/acquire或显式 barrier - Clang/GCC 在 ARM64 下对
relaxedstore 常生成str w0, [x1]+dmb ish;x86-64 下通常只有mov dword ptr [rdi], esi
std::memory_order_seq_cst 在 ARM 上代价显著更高
x86 天然提供顺序一致性(SC)语义,seq_cst load/store 通常不额外生成 barrier 指令(仅部分 store 可能加 mfence)。ARM 必须为每个 seq_cst 操作插入 full barrier(dmb ish),且 load-use 和 store-store 之间还需配对控制(例如 ldar/stlr 指令本身带 acquire/release 语义,但组合成 SC 需额外同步)。
// ARM64 Clang 15 -O2 生成的 seq_cst store mov x8, #1 stlr w8, [x0] // store-release dmb ish // 强制全局顺序,x86 下这行通常不存在
- 频繁使用
seq_cst会显著拖慢 ARM 多核性能,尤其在高争用计数器场景 - 若逻辑只需 acquire-release 语义(如锁、状态标志),应显式用
memory_order_acquire/release,避免无谓升级为seq_cst - 注意:GCC/Clang 对
seq_cst的优化策略不同,ARM 下 GCC 更倾向插入dmb,Clang 可能复用ldar/stlr的隐含语义,但跨操作的顺序仍需 barrier
ARM 的 ldar/stlr 指令不等于 x86 的 mov + 缓存一致性
x86 的 cache coherency 协议(MESIF/MOESI)天然保证所有核看到一致的写顺序,而 ARM 的 ldar(load-acquire)和 stlr(store-release)是**语义指令**,它们不保证全局顺序,只约束当前核的指令重排,并配合 dmb 实现跨核同步。误以为 “用了 stlr 就自动全序” 是常见误区。
立即学习“C++免费学习笔记(深入)”;
-
stlr保证该 store 不会重排到其后的任何访存之前,但不保证其他核立即看到 —— 还需 cache line 的 write-back 和 snoop 响应 - ARMv8.3+ 引入
LDAPR(load-acquire, prefetch)等变种,但标准std::atomic实现不依赖这些扩展 - 调试时若发现 ARM 上原子变量更新延迟可见,先检查是否混用了 non-atomic 访问(破坏了 compiler barrier),再确认是否漏了 acquire/release 匹配
std::atomic 代码时,最易被忽略的是:你以为的“安全重排”在 ARM 上根本不会发生,而你以为的“自然顺序”在 ARM 上必须靠显式内存序兜底。别依赖 x86 的宽容去验证正确性。










