3 ms·
`rep; nop;` is actually the `pause` instruction. On older CPUs it’s a standard nop, but on newer CPUs it’s a more efficient nop. Spinning on the CMPXCHG is als
by secondcoming 2y ago
`rep; nop;` is actually the `pause` instruction. On older CPUs it’s a standard nop, but on newer CPUs it’s a more efficient nop.
Spinning on the CMPXCHG is also a bad idea. You should spin on the read and only then attempt the CMPXCHG.
- bobmcnamara 2y agoBingo. Spinning on CMPXCHG can cause livelock.
- epcoa 2y agoJust to clarify: the spinning on CMPXCHG is not also a bad idea, the YieldProcessor is correct (a pause), but inside the CMPXCHG loop it should be spinning on a pure unlocked load. Is that correct?
- secondcoming 2y agoNo you should spin on a read. Once you see the value you want you then try the CMPXCHG. If that succeeds you exit. If it fails you go back to spinning on the read.
- epcoa 2y agoWhat is the difference between a read and a “load” here?
- loeg 2y agoRead and load mean the same thing. (I think GP just missed the end of your comment.) You care about exchange vs read/load because of cache line ownership. Every time you try to do the exchange, the attempting CPU must take exclusive ownership of the cacheline (stealing it from the lock owner). To unlock, the lock owner must take it back. If the attempting CPU instead only reads, the line ownership stays with the lock holder and unlock is cheaper. In general you want cache line ownership to change hands as few times as possible.
- pizlonator 2y agoSpinning with pause is slower than spinning with sched_yield according to every test I’ve ever done
- epcoa 2y agoFor what it is worth it seems the library in question does both, uses an exponential retry loop, busy read looping 2^i times for the first 7 attempts before then yielding. It seems like there must be some threshold where latency is improved by retrying before yielding, but I don’t test these things for a living. https://github.com/google/nsync/blob/c6205171f084c0d3ce3ff51f1d7da8e9aa60d11e/internal/common.c#L87 https://github.com/google/nsync/blob/c6205171f084c0d3ce3ff51...
- pizlonator 2y agoMy data says that always yielding is better than ever busy spinning, except on microbenchmarks, where depending on the microbenchmarks you can get any answer your heart desires.
- Animats 2y agoI've tried to figure out where that goes wrong. Read the bug report linked. I've caught this situation in gdb with 20 threads in spinlocks inside realloc, performance in a game down to 1 frame every 2 seconds, and very little getting done. But I don't understand how the three levels of locking involved cause that. Nor do the Wine people. Works fine on Microsoft Windows. Only fails on Wine.
- epcoa 2y agoIt seems like the loop around InterlockedCompareExchange is a bad idea since this is a bus lock in a tight loop. Rather the inner spinning loop that is yielding should just be reading the value surrounded by the cmpxchg. As for whether sched_yield should just be called in the inner loop or a short nop/pause loop should be attempted for microcontention reasons, the expert opinion here is don't bother with the nop loop. However while the nop loop might not be real world optimal I doubt that would be causing a catastrophic performance issue.