![](/img/trans.png)
[英]Atomic exchange of two std::atomic<T*> objects in a lock-free manner in C++11?
[英]Why don't standard libraries implement std::atomic for structs under 8 bytes in a lock-free manner?
假设该体系结构可以为std :: atomic以无锁方式支持8字节标量。 为什么标准库不为8字节以下的结构提供类似的特化?
这种std :: atomic特化的简单实现只能将结构序列化/反序列化(使用std::memcpy
)到等效的std::uintx_t
,其中x
是结构的宽度(以位为单位)(舍入为最接近的幂) 2大于或等于结构的宽度)。 这将被很好地定义,因为std :: atomic需要这些结构是可以轻易复制的。
例如。 https://godbolt.org/z/sxSeId ,这里Something
只有3个字节,但实现调用__atomic_load
和__atomic_exchange
,两者都使用锁表。
使用struct Something { alignas(4) char a; char b,c; };
struct Something { alignas(4) char a; char b,c; };
(不是alignas(4) char a,b,c;
因为这会使每个 char填充为4个字节,因此它们可以各自对齐。)
具有非2次幂大小的对象可能跨越高速缓存行边界,因此使用更宽的4字节负载并不总是可行的。
加上纯商店总是必须使用CAS(例如lock cmpxchg
)来避免发明写入对象外的字节:显然你不能使用两个单独的mov
存储(2字节+ 1字节),因为这不会是原子的,除非你在带有重试循环的TSX事务中这样做。
对于不跨越8字节边界的内存访问,x86加载/存储仅保证原子性。 (在一些供应商/搜索中,缓存行边界。或者对于可能不可缓存的加载/存储,基本上自然对齐是您需要的)。 为什么在x86上对自然对齐的变量进行整数赋值?
你的struct Something { char a, b, c; };
struct Something { char a, b, c; };
没有对齐要求所以没有C ++规则阻止Something
对象跨越2个缓存行。 这将使个纯mov
它加载/存储绝对非原子。
gcc和铛选择实现atomic<T>
用相同的布局/对象的表示为T
(无论是无锁的或没有)。 因此, atomic<Something>
是一个3字节的对象。 因此, atomic<Something>
的数组必然会有一些跨越缓存行边界的对象,并且不能在对象外部填充,因为这不是数组在C中的工作方式sizeof()
= 3告诉您数组布局。 这使得无锁atomic<Something>
变得不可能。 (除非你使用lock cmpxchg
加载/存储即使在缓存行拆分时也是原子的,否则会在发生这种情况时产生巨大的性能损失。更好地让开发人员修复它们的结构。)
atomic<T>
类可以具有比T
更高的对齐要求,例如atomic<int64_t>
具有alignof(atomic_int64_t)== 8,与许多32位平台(包括i386 System V alignof(int64_t) == 4
上的alignof(int64_t) == 4
不同ABI)。
如果gcc / clang没有选择保持布局相同,那么他们可能有atomic<T>
垫小对象,直到2的下一个幂并添加对齐,这样它们就可以无锁。 这将是一个有效的实现选择。 我想不出任何缺点。
有趣的是,gcc的C11 _Atomic
支持在具有64位无锁原子的32位平台上略有破坏 : _Atomic _Atomic int64_t
可能在结构内部未对齐导致撕裂。 他们仍然没有更新_Atomic
类型的ABI以具有自然对齐。
但是g ++的C ++ 11 std :: atomic在一个标题中使用了一个模板类来修复那个bug( https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65147 ); 确保atomic<T>
具有自然对齐(最大为2个大小的幂),即使T
具有对齐<大小。 因此,他们无法跨越任何边界。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.