std::atomic<> 是否保证 store() 操作立即(几乎)传播到具有 load() 的其他线程?

Does std::atomic<> gurantee that store() operation is propagated immediately (almost) to other threads with load()?

我有 std::atomic<T> atomic_value;(对于类型 T 是 bool,int32_t,int64_t 和任何其他)。如果第一个线程

atomic_value.store(value, std::memory_order_relaxed);

在第二个线程中我做的某些代码点

auto value = atomic_value.load(std::memory_order_relaxed);

这个更新的原子值在 CPU 个内核之间从第一个线程传播到第二个线程的速度有多快? (对于所有 CPU 型号)

它几乎是立即传播的吗?例如 Intel 高速缓存一致性传播的速度,这意味着 0-2 个周期左右。其他一些 CPU models/manufacturers.

可能还有几个周期

或者这个值有时可能会在很多很多周期内都没有更新?

对于给定的 CPU,atomic 是否保证值在 CPU 核心之间尽可能快地传播?

也许如果我在第一个线程上做

atomic_value.store(value, std::memory_order_release);

和第二个线程

auto value = atomic_value.load(std::memory_order_acquire);

那么它是否有助于更快地传播价值? (注意两个内存顺序的变化)现在有速度保证?还是和放宽顺序一样保证速度?

作为附带问题 - 用 release+acquire 替换 relaxed order 是否也会同步其他(非原子)变量中的所有修改?

意味着在第一个线程中,在随释放存储之前写入内存的所有内容,是否保证在第二个线程中的整个内存在加载时完全处于最终状态(与第一个线程中相同) -获取,当然是在加载值是新值(更新)的情况下。

因此,这意味着对于任何类型的 std::atomic<>(或 std::atomic_flag),一个线程中的存储与释放点同步它之前的所有内存写入与另一个线程中的点如果在其他线程中更新了原子的值,那么它会加载并获取相同的原子吗? (当然,如果第二个线程中的值还不是新的,那么我们预计内存写入尚未完成)

PS。为什么会出现问题...因为根据名称 "atomic" 很明显可以得出结论(可能未得出结论)默认情况下(没有额外的限制,即只是放宽了内存顺序) std::atomic<> 只是使得任何算术运算都是原子的,没有别的,没有其他关于同步或传播速度的保证。这意味着对内存位置的写入将是完整的(例如 int32_t 一次写入所有 4 个字节),或者与原子位置的交换将以原子方式进行读写(实际上以锁定方式),或者递增一个值就可以了原子地三个操作读-加-写。

C++ 标准只说这个 [C++20 intro.progress p18]:

An implementation should ensure that the last value (in modification order) assigned by an atomic or synchronization operation will become visible to all other threads in a finite period of time.

从技术上讲,这只是一个“应该”,“有限时间”并不是很具体。但是 C++ 标准足够广泛,您不能指望它们指定特定的周期数或纳秒数或您拥有的东西。

实际上,您可以预期调用任何原子存储函数,即使使用 memory_order_relaxed,也会导致执行实际的机器存储指令。该值不仅会保留在寄存器中。在那之后,它不在编译器的手中,直到 CPU.

(从技术上讲,如果您对同一个对象有两个或多个连续存储,并且在其间完成了一定数量的其他工作,则允许编译器优化除最后一个以外的所有内容,基于无论如何,您无法确定任何给定的加载是否会在正确的瞬间发生以查看其他值之一。实际上,我不相信目前有任何编译器这样做。)

对典型 CPU 架构的合理期望是商店将“没有不必要的延迟”变得全局可见。存储可以进入核心的本地存储缓冲区。核心将尽快处理存储缓冲区条目;它不仅让他们坐在那里像美酒一样陈年。但他们仍然需要一段时间。例如,如果缓存行当前由另一个核心独占,您将必须等到它被释放才能提交您的存储。

使用更强的内存排序不会加快进程;机器已经在尽最大努力提交商店。事实上,更强的内存排序实际上可能会减慢它的速度;如果存储是按发布顺序创建的,那么它必须等待缓冲区中所有较早的存储都提交,然后才能自己提交。在像 x86 这样的 strongly-ordered 架构上,每个存储都会自动释放,因此存储缓冲区始终保持严格的顺序;但是在弱排序的机器上,使用宽松的排序可能会让你的商店“插队”并比其他方式更快地到达 L1 缓存。