无锁原子在实践中是无地址的

Are lock-free atomics address-free in practice?

本文关键字:无地址 实践中      更新时间:2023-10-16

Boost。Interprocess是一个很棒的库,它简化了不同进程之间共享内存的使用。它提供了互斥、条件变量和信号量,允许在写入和读取共享内存时进行同步。

然而,在某些情况下,这些(相对)性能密集型同步机制是不必要的——原子操作就足以满足我的用例,并且可能会提供更好的性能。

不幸的是,Boost。Interprocess似乎没有原子论。


C++标准库提供了std::atomic类模板,它封装了操作需要是原子操作的对象,还具有测试原子操作是否无锁的功能。但它并不要求无锁原子也无地址:[atomics.lockfree]/4只是鼓励免锁操作无地址,这与cpprreference一致。

我想不出有什么理由可以用非地址自由的方式实现无锁原子。在我看来,以无地址的方式实现无锁原子甚至要容易得多。

由于当使用原子而不是互斥(来自Boost.Interprocess)时,我会获得显著的性能优势,因此在这里忽略标准遵从性并将std::atomic对象存储在共享内存中似乎很诱人。


这个问题有两个部分:

  1. CPU在实践中是否以无地址的方式实现无锁原子?(我只关心用于运行现代桌面和移动操作系统(如Windows、MacOS、Linux、Android、iOS)的CPU,而不关心嵌入式系统)
  2. 一个实现究竟为什么要使用无锁的非地址自由原子

是的,无锁原子在所有普通CPU上的所有C++实现上都是无地址的,并且可以安全地在进程之间的共享内存上使用不过,非锁自由原子1在进程之间是不安全的。每个进程都有自己的锁哈希表(std::atomic的锁在哪里?)。

C++标准打算让无锁原子在进程之间的共享内存中工作,但它只能做到"无锁";应该";而没有定义术语等等。

C++草案29.5无锁属性

[注意:无锁定的操作也应该是无地址的。也就是说,通过两个不同的地址对同一内存位置进行的原子操作将进行原子通信。实现不应依赖于任何每个进程的状态此限制允许通过多次映射到进程中的内存和两个进程之间共享的内存进行通信--尾注]

这是一个很容易在当前硬件上实现的实现质量建议,事实上,你必须努力设计一个deathstation9000 C++实现,该实现在x86/ARM/PPowerPC/其他主流CPU上违反了它,同时实际上是无锁的。


硬件为原子读-修改-写操作公开的机制基于只关心物理地址的MESI缓存一致性。x86lock cmpxchg/lock add/等使得核挂起处于修改状态的高速缓存线,因此在原子操作的中间没有其他核可以读取/写入该高速缓存线。(对于';int num';,num++可以是原子的吗?)。

大多数非x86体系结构使用LL/SC,它允许您编写一个重试循环,该循环仅在存储是原子的情况下执行存储。LL/SC可以在不引入地址的情况下以无等待的方式用O(1)开销来模拟CAS。

C++无锁原子编译直接使用LL/SC指令。关于x86示例,请参阅我对num++问题的回答。有关使用LL/SC指令的compare_exchange_weakfetch_add的AArch64代码生成的一些示例,请参阅原子清除无符号整数的最低非零位。

原子纯加载或纯存储更容易,并且可以通过对齐的数据免费进行。在x86上,请参阅为什么在x86上自然对齐变量上的整数赋值是原子的?其他国际会计准则也有类似的规则。


相关:我在Genuinly测试std::atomic是否无锁的答案中包含了一些关于地址自由的评论。我不确定它们是有用的还是正确的


脚注1

所有主流CPU都为指针宽度以下的对象提供了无锁原子。有些具有更宽的原子,如x86具有lock cmpxchg16b,但并非所有实现都选择将其用于双宽度无锁原子对象。检查C++17std::atomic::is_always_lock_freeATOMIC_xxx_LOCK_FREE(如果已定义)以进行编译时检测。

(有些微控制器无法在单个寄存器中保存指针(或通过单个操作复制指针),但通常没有此类ISA的多核实现。)


为什么一个实现会使用无锁的非地址自由原子?

我不知道有什么合理的理由让硬件像普通的现代CPU一样工作。你可以想象一些架构,通过将地址提交给某个来进行原子操作

我认为C++标准希望尽可能避免约束非主流实现。例如在某种解释器之上的C++;正常的";CPU架构。

IDK,如果你可以在一个松散耦合的共享内存系统上有效地实现C++,比如一个带有以太网链路的集群,而不是共享内存,或者非一致共享内存(必须显式刷新才能让其他线程看到你的存储)。

我认为,如果不假设实现将在多个进程可以设置共享内存的操作系统下运行程序,C++委员会就不能对原子必须如何实现说太多。

他们可能在想象未来的ISA,在那里不可能实现无地址原子,但我认为他们更有可能不想谈论多个C++程序之间的共享内存。该标准只要求一个实现运行一个程序。

显然std::atomic_flag实际上保证是无地址的为什么只有std::atomic_flag保证是无锁的?,所以IDK为什么他们不对实现选择实现为无锁的任何CCD_ 12提出相同的要求。