wait-free
wait-free 不是更快的 lock-free:它把 CAS 失败从“再来一次”变成“我排在你后面”,每个线程的步数因此有了上界,代价是更多原子操作和更难写对的代码。
标签
6 篇
wait-free 不是更快的 lock-free:它把 CAS 失败从“再来一次”变成“我排在你后面”,每个线程的步数因此有了上界,代价是更多原子操作和更难写对的代码。
自旋锁快不快,看等锁的核在干什么:每次都读改写,就把锁所在的行抢来抢去;先读后抢、循环里加 PAUSE、抢输了退一步,等的时候才几乎不打扰持锁的核。
MESI 只执行一条规矩:一条缓存行,要么只有一个核能写,要么有任意多个核只能读,两者不会同时存在。四个状态就是两个问题的答案——别人有没有这一行?我改过没有?协议里的每一笔代价,不是在层与层之间搬数据,就是在等另一个核确认。伪共享、SPSC 队列的缓存游标、读者越多生产者越慢,都是同一台状态机。一致性仍然不保证多字读取不撕裂,也不管不同地址的写谁先谁后,那是内存屏障的事。
行情只有一个生产者,却有好几个消费者,每个都要看到每一条,而生产者是热路径,不能等任何人。答案是广播环:每个槽位自带序号戳,读者各持私有游标,生产者写满一圈就覆盖最老的槽位,从不读读者的状态。代价转给了读者:套圈是无声的;读完再复查一次戳,只有在写者先作废戳的前提下才抓得到写了一半的槽位;追上来的读者会抢走生产者要写的那一行,全速写时每条都变贵。放进共享内存再加三条:元素里不能有任何指针,第一圈每 32 条碰一次缺页,读者重启不存游标就全丢。
无锁 SPSC 队列成立,是因为每个共享变量只有一个写者:每一边只写自己的下标,只读对方的。剩下的工作是别让这个性质被悄悄破坏:元素个数用两个下标相减而不是共享计数器,对方的下标缓存一份、只在看起来满了或空了时才刷新,日志按文本段推而不是按字符推。SPSC 只在“失败就返回”的接口下才是 wait-free,有界队列满了总得在丢弃和阻塞之间选一个。

同一套缓存硬件带出两个不相干的问题。正确性:写先进 store buffer,别的核要过一段时间才看得见,靠 release/acquire 配对补上顺序。性能:就算每个值都读对了,两个核抢同一条缓存行(伪共享)照样慢,靠缓存行布局解决。