首先,原子加载/存储与常规存储在编译为 asm 时并没有什么特别之处。 (虽然默认的seq_cst 内存顺序可以编译为xchg,但mov+mfence 也是一个有效的(通常较慢)选项,它在asm 中与普通的发布存储后跟一个完整的屏障没有区别。) xchg 是一个原子 RMW + 一个完整的屏障。编译器使用它来实现全屏障效果;交换的负载部分只是一个不需要的副作用。
此答案的其余部分完全适用于任何 x86 asm 存储,或内存目标 RMW 指令的存储部分(无论它是否是原子指令)。
假设它尚未被逐出到 L2 或 L3,最初之前一直在执行写入的内核将在其 L1d 中将行置于 MESI Modified 状态。
该行更改 MESI 状态(到共享)以响应读取请求,或者对于存储执行写入的核心将发送 RFO(所有权请求)并最终使该行处于修改或独占状态。
在现代 Intel CPU 上的物理内核之间获取数据始终涉及到共享 L3(不一定是 DRAM)的写回。我认为即使在多插槽系统上也是如此,其中两个内核位于不同的插槽上,因此它们并不真正共享一个公共 L3,使用窥探(和窥探过滤)。
英特尔使用 MESIF。 AMD 使用 MOESI,它允许直接在内核之间直接共享脏数据,而无需先写回共享的外层缓存。
更多详情请见Which cache mapping technique is used in intel core i7 processor?
存储数据无法到达另一个核心,除非通过缓存/内存。
您对“发生”在另一个内核上的写入的想法并不是任何事情的运作方式。在尊重 x86 内存排序规则的同时,我什至看不到它是如何实现的:来自一个内核的存储在程序顺序中变得全局可见。我不明白如何将存储(到不同的缓存行)发送到不同的内核,并确保一个内核等待另一个内核将这些存储提交到它们各自拥有的缓存行。
即使对于弱序 ISA,它也不是很合理。通常,当您读取或写入高速缓存行时,您将进行更多的读取+写入。通过内核之间的网状互连分别发送每个读取或写入请求将需要许多微小的消息。高吞吐量比低延迟更容易实现:更宽的总线可以做到这一点。负载的低延迟对于高性能必不可少。如果线程曾经在内核之间迁移,突然之间,它们将在其他内核上读/写 L1d 中的所有热缓存行,这将是可怕的,直到 CPU 以某种方式决定它应该将缓存行迁移到内核访问它。
L1d 缓存体积小、速度快且相对简单。相对于彼此对核心的读取和写入进行排序以及进行推测加载的逻辑都在单个核心内部。 (存储缓冲区,或者在 Intel 上实际上是一个内存顺序缓冲区,用于跟踪推测加载和存储。)
这就是为什么你应该避免接触一个共享变量,如果你能证明你没有必要的话。 (或者在适当的情况下使用指数退避)。以及为什么 CAS 循环应该在尝试 CAS 之前以只读方式等待查看它正在寻找的值,而不是在 lock cmpxchg 尝试失败时写入缓存行。