C++11:16 字节原子<>变量是否在 16 字节边界上自动对齐,从而允许CMPXCHG16B指令?

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

本文关键字:字节 对齐 指令 CMPXCHG16B lt gt 变量 边界 C++11 是否      更新时间:2023-10-16

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

任何像样的std::atomic<>实现都将使用alignas本身来使lock cmpxchg16b高效,如果库完全使用lock cmpxchg16b而不是 16 字节对象的互斥锁。

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

你不需要alignas(16)atomic<T>.

仅当您有一个要atomic_ref使用的纯T对象时,才需要手动对齐原子。atomic_ref<>没有对齐现有 T 对象的机制。 设计的当前版本公开了应使用的required_alignment成员。 为了正确性,这取决于你。 (否则,你会得到UB,这可能意味着撕裂,或者只是拆分lockRMW的极慢的系统范围性能。

// 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的原子性保证不取决于对齐(。 更像是实际的总线锁,而不仅仅是延迟MESI响应的本地到此核心缓存锁。

较窄的原子肯定需要自然对齐以确保正确性,因为纯负载和纯存储可以编译为 asm 纯负载或存储,其中硬件保证需要一些对齐。

但是 16 字节对象只能保证原子lock cmpxchg16b因此.load().store()必须通过lock cmpxchg16b来实现。 (使用 CAS(0,0( 加载以获取旧值,然后用自身替换 0 或不执行任何操作,并使用 CAS 重试循环进行存储。 这很糟糕,但比互斥锁好一些。 它没有你期望从无锁load中获得的读取侧可扩展性,这是GCC7及更高版本不再将atomic<16-byte-object>宣传为无锁的原因之一,即使它仍将在它调用的libatomic函数中使用lock cmpxchg16b而不是内联lock cmpxchg16b