Skip to content
Tech News
← Back to articles

Optimizing a Spin-Lock

read original more articles
Why This Matters

This story highlights advancements in spin-lock optimization, demonstrating how a carefully designed lock can significantly improve performance and energy efficiency. Such improvements are crucial for high-performance computing and multi-threaded applications, where locking mechanisms often become bottlenecks.

Key Takeaways

Squeezing every pico out of the simplest lock.

A spin-lock is a lock that never sleeps. Instead of yielding to the scheduler, the thread stays on the CPU and spins. No syscalls. No context switches. In this post, we’ll build a version, step by step, that is 5.7x faster while drawing 5.4x less energy.

Threads increment a shared counter under the lock.1

template < typename Lockable > auto BM_SpinLock ( benchmark :: State & state ) -> void { alignas ( std :: hardware_destructive_interference_size ) static auto lockable = Lockable {}; alignas ( std :: hardware_destructive_interference_size ) static auto counter = std :: uint64_t {}; pinThread ( state . thread_index ()); for ( auto _ : state ) { lockable . lock (); ++ counter ; lockable . unlock (); } benchmark :: DoNotOptimize ( counter ); }

The lock and the counter get a cache line each. Threads are pinned.

A naive spin-lock §

An atomic bool and an exchange loop.2

class SpinLockV1 { std :: atomic_bool locked_ { false }; public : auto lock () noexcept -> void { while ( locked_ . exchange ( true )); } auto unlock () noexcept -> void { locked_ . store ( false ); } };

Uncontended it takes 3.14 ns. Two threads take 61.5 ns, twenty times as long. Four take 246 ns.

$ ./benchmark --benchmark_filter = 'V1>' BM_SpinLock<SpinLockV1>/real_time/threads:1 3.14 ns BM_SpinLock<SpinLockV1>/real_time/threads:2 61.5 ns BM_SpinLock<SpinLockV1>/real_time/threads:4 246 ns

... continue reading