内存可见性延迟是CPU核心修改变量后其他核心看到新值所需时间,由缓存一致性协议决定,通常为数十至数百纳秒;lat_mem_rd无法测量它,因其仅测单核本地访问延迟,不涉及跨核同步。

内存可见性延迟不是单次读写耗时,而是指一个 CPU 核心修改了某个变量后,其他核心“看到”这个新值所需的时间。它不等于缓存延迟,也不等于总线传播时间,而是由缓存一致性协议(如 x86 的 MESI、ARM 的 MOESI)触发的完整同步链路决定的——包括写入本地缓存、标记状态变更、广播无效请求(Invalidate)、等待响应、刷新远程缓存行等步骤。在现代多核系统中,这个过程通常落在 数十纳秒到数百纳秒量级,具体取决于架构、缓存层级、竞争程度和是否跨 NUMA 节点。
为什么不能直接用 lat_mem_rd 测可见性?
lmbench 的 lat_mem_rd 测的是单核访问不同内存区域的延迟(L1/L2/主存),它不涉及多核间状态同步,因此无法反映可见性延迟。它测的是“我能多快读到自己刚写的值”,而可见性问的是“别的核多久能读到我写的值”。两者底层机制完全不同:
-
lat_mem_rd绕过写缓冲区与一致性协议,只走本地缓存路径 - 可见性测试必须触发缓存行状态迁移(如从 Shared → Modified → Invalid),强制跨核通信
- 若变量未被其他核缓存(cold state),首次写入后另一核第一次读可能触发完整的 cache line fill + coherence handshake,耗时更长
实战中可复现的典型测试方法
要真实测量可见性延迟,需构造可控的跨核写-读场景,常见可靠方式有:
- 乒乓式原子计数器:两个核心交替对同一变量执行 fetch_add(1),用高精度时间戳(如 rdtscp 或 clock_gettime(CLOCK_MONOTONIC_RAW))记录每次操作前后时间差,取稳定段的间隔中位数。典型结果:同 socket 内约 30–80 ns;跨 NUMA node 可达 150–400 ns
-
信号量握手循环:Core A 写值 + 写标志位(volatile 或 atomic),Core B 自旋等待标志就绪后读值并回写确认。用 TSC 差值统计从 A 写完到 B 读到的延迟。关键要禁用编译器重排(
atomic_thread_fence)和确保标志位不在同一缓存行(避免伪共享) -
Linux kernel 的
tools/testing/selftests/mm/soft-dirty或membarrier测试套件:它们内置跨核可见性验证逻辑,可直接运行并输出统计分布
影响可见性延迟的关键因素
实测中你会发现,相同硬件下结果波动很大。主要干扰项包括:
- 缓存行位置:若变量恰好与其他频繁修改的变量共享缓存行(64 字节),会因伪共享导致反复无效化,延迟飙升至微秒级
-
NUMA 拓扑:MLC 工具的
--latency_matrix输出可查证节点间延迟差异。例如 node0→node1 延迟为 130 ns,而 node0→node0 仅 82 ns,说明跨节点同步天然更慢 - 写缓冲区与 store forwarding:x86 允许本核 store 后立即 load(store forwarding),但这对其他核不可见;只有当 store 提交到 L1d 并触发 coherence action 后,才开始可见性倒计时
-
中断与调度干扰:测试线程若被抢占,测量窗口拉长。建议绑核(
taskset -c 0,1)、关闭 CPU 频率调节(cpupower frequency-set -g performance)、禁用 NMI watchdog
一个快速验证的 C++ 片段(同 socket 双核)
无需复杂框架,以下精简代码即可观察量级(需用 std::atomic<uint64_t></uint64_t> + memory_order_relaxed 写 + memory_order_acquire 读,并确保两线程绑定固定核心):
// Core 1:检测到 flag == 1 后读目标变量,设 reply = 1
// 记录从 flag=1 到 reply=1 的 TSC 差值,重复万次取 P99

















