C++11:16 字节 atomic<> 变量是否在允许 CMPXCHG16B 指令的 16 字节边界上自动对齐?

C++11: are 16-byte atomic<> variables automatically aligned on 16-byte boundaries allowing CMPXCHG16B instruction?

16 字节 atomic<> 变量是否在 16 字节边界上自动对齐,从而允许 compiler/runtime 库有效地使用 x86 CMPXCHG16B 指令?还是我们应该始终为所有此类变量手动指定 alignas(16)

如果库使用 lock cmpxchg16b 而不是 16 字节的互斥量,std::atomic<> 的任何体面的实现都将使用 alignas 本身来提高 lock cmpxchg16b 的效率对象。

并非所有实现都如此,例如,我认为 MSVC 的标准库使用标准互斥回退使 16 字节对象完全无锁。

您不需要 alignas(16) atomic<T>

如果您有一个要在其上使用 atomic_ref 的普通 T 对象,则只需要手动对齐原子。 atomic_ref<> 没有对齐现有 T 对象的机制。当前版本的设计公开了一个您应该使用的 required_alignment 成员。为了正确性,由你来做。 (否则你会得到 UB,这可能意味着撕裂,或者 极度 拆分 lock RMW 的系统范围性能缓慢。)

 // for atomic_ref<T>
alignas(std::atomic_ref<T>::required_alignment) T sometimes_atomic_var;

 // often equivalent, and doesn't require checking that atomic_ref<T> is supported
alignas(std::atomic<T>) T sometimes_atomic_var;
 // use the same alignment as atomic<T>

请注意,跨高速缓存行边界的未对齐 lock cmpxchg16b 拆分仍然是原子的,但非常非常慢(与任何 locked 指令相同:原子 RMW 的原子性保证不取决于结盟)。更像是一个实际的总线锁,而不仅仅是一个本地到这个核心 .

更窄的原子肯定需要自然对齐以确保正确性,因为纯加载和纯存储可以编译为

但是 16 字节的对象只有 lock cmpxchg16b 保证是原子的,所以 .load().store() 必须用 lock cmpxchg16b 来实现。 (加载 CAS(0,0) 以获取旧值,然后将 0 替换为自身或什么都不做,然后使用 CAS 重试循环进行存储。这很糟糕,但比互斥锁要好一些。它没有读取 -您期望从无锁 load 获得的侧面可伸缩性,这是 GCC7 和后来不再将 atomic<16-byte-object> 宣传为无锁的原因之一,即使它仍将使用 lock cmpxchg16b它调用的 libatomic 函数而不是内联 lock cmpxchg16b)