4 ms·
I was optimizing some code where, for reasons, many threads had to aggregate data in a shared data structure. Each thread would have a small buffer, and when fu
by magicalhippo 3y ago
I was optimizing some code where, for reasons, many threads had to aggregate data in a shared data structure. Each thread would have a small buffer, and when full it would acquire a lock and aggregate.
While fooling around I had the idea to exploit that not all atomic operations are equal. So I added an additional "contention" flag. When a thread wanted to aggregate it would do an atomic read of the flag, if it was set it would bail[1] and continue to accumulate to the local buffer. Once done aggregating the flag would be reset after unlocking.
Effectively this was adding a single-iteration spinlock before the "heavy" lock, but even using CriticalSections for the lock on Windows (which does spin before acquiring a mutex lock) it resulted in clear improvements, especially when running on machines with more than 8 cores.
[1]: It would bail unless the local buffer grew too large, so slight memory vs perf tradeoff there.
- loeg 3y agoThis is more or less equivalent to a try_lock() operation, yeah? And continuing to collect to the local buffer if try_lock fails, up to some limit.
- magicalhippo 3y agoYeah I suppose. One limitation we had was cross-compiling on Windows, Linux and OSX so available locking primitivea differed, so was easier to just do it via atomics.