bigywater.tech
Rust · 92

原子类型与内存顺序

内存序是 Rust 并发里最难直觉的部分。这篇先证明原子操作与原子逻辑不是一回事,再测不同 Ordering 的成本,最后在 aarch64 上真的跑出 Relaxed 的异常结果,让 Release / Acquire 的作用变得可见。

1005154
load + store 版计数器的最终值(期望 4000000)
672 次
Relaxed 下,单次运行(100 万轮)里出现的最大异常次数
0 次
Release / Acquire 在所有运行里的异常次数

原子操作 ≠ 原子逻辑

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    fetch_add:      期望 4000000,实际 4000000
    load + store:   期望 4000000,实际 1005154(丢失了更新)

Ordering 的成本

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    Relaxed  单线程  1.93 ns/次   4 线程争用  18.93 ns/次(每次操作的平均墙钟时间)
    AcqRel   单线程  3.74 ns/次   4 线程争用  30.02 ns/次(每次操作的平均墙钟时间)
    SeqCst   单线程  3.74 ns/次   4 线程争用  29.11 ns/次(每次操作的平均墙钟时间)

耗时是本机微基准,各次测量之间有噪声。

Relaxed 真的会乱序

实测 · 本机执行输出
  编译:成功
  第 1 次运行(每次 100 万轮):Relaxed/Relaxed 异常 2 次;Release/Acquire 异常 0 次
  第 2 次运行(每次 100 万轮):Relaxed/Relaxed 异常 24 次;Release/Acquire 异常 0 次
  第 3 次运行(每次 100 万轮):Relaxed/Relaxed 异常 3 次;Release/Acquire 异常 0 次
  第 4 次运行(每次 100 万轮):Relaxed/Relaxed 异常 672 次;Release/Acquire 异常 0 次

下一篇讲 CAS:Atomic CAS 操作。

分步演示

原子类型与内存序
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。aarch64(弱内存模型);release 构建
    • 没有验证:x86_64 上的 Relaxed 行为、SeqCst 与 AcqRel 在存储缓冲(SB)测试里的区别、fence 的用法、Consume 序(Rust 没有)、其它架构、更大的线程数下的争用成本
    京ICP备2026067502号-1京公网安备11010502063487号