哪些硬件条件会导致原子操作fetch_add(RMW,即读取-修改-写入)显著阻塞控制流?

编程语言 2026-07-10

在C++ (std::atomic::fetch_add) 中,我们经常把原子性的Read-Modify-Write(RMW)操作当作快速、“无锁”的原语。然而,我对在硬件层面理解这些操作的 极端延迟边界 感兴趣。

除了明显的高线程争用(缓存行在核心之间来回跳动)这一情形外,我还想知道:是否有可能观察到单次fetch_add操作让CPU的指令流水线长时间停滞?如果可能,是什么样的硬件或系统级条件会触发,即使原子变量并未遭到大量争用?

例如:

#include <thread>
#include <atomic>

int main(){
  std::atomic<int> v = 0;

  auto t1 = std::thread([&](){
     v.fetch_add(1,std::memory_order::relaxed); // #1
  });

  auto t2 = std::thread([&](){
     v.fetch_add(1,std::memory_order::relaxed); // #2
  });
  t1.join();
  t2.join();
}

我们是否能观察到对 #1 的完整调用花费很长时间,或对 #2 的完整调用花费很长时间?

解决方案

为什么原子RMW在争用之外也会变慢(Beyond Contention)

  1. 缓存未命中 / DRAM延迟 如果原子变量不在任何缓存层级(冷缓存或被置换),CPU必须从DRAM取回它,才能执行RMW。这单独可能花费约100–300+纳秒(相比于L1缓存约4 纳秒)。
  2. NUMA(非统一内存访问) 在多插槽系统中,如果缓存行驻留在远端NUMA节点,取数必须跨越互连(如Intel QPI/UPI或 AMD Infinity Fabric)。这会增加显著延迟——可能比本地DRAM访问慢2–5倍。
  3. 缓存行状态机(MESI/MOESI协议) 即使没有争用,原子RMW仍需要缓存行处于Exclusive(独占)或Modified(修改)状态。如果处于Shared(共享)状态(即使来自另一核心的一次读取),CPU也必须发出失效请求并等待确认后再继续。
  4. 内存总线 / 互连拥塞 在负载较高的系统中,内存控制器或互连可能被其他流量(如DMA、其他核心执行内存密集型工作)饱和,导致原子操作的总线传输进入排队状态。
  5. CPU微架构序列化 在x86架构上,带锁前缀的指令(支持fetch_add的指令)充当完整的内存屏障,可能会对写缓冲区进行序列化。如果写缓冲区前面有大量待处理的写入,CPU必须先清空它们。
  6. SMM(系统管理模式)中断 固件可以触发SMI(System Management Interrupt),这会暂停所有CPU核心并进入SMM。对软件不可见,但可能给任何指令(包括原子操作)带来微秒到毫秒级的不可解释延迟。
  7. 热限制引起的降频 / 电源管理 如果CPU因热限制而降频,或在P-states/C-states之间切换,时钟频率下降会扩大任何操作的实际耗时。
  8. 原子地址的TLB未命中 如果原子变量的虚拟地址到物理地址的映射不在TLB中,必须完成一次页表遍历后才能进行内存访问,从而增加延迟。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章