Why Most Spinlock Authors Misinterpret the PAUSE Instruction
The article explains that the PAUSE (F3 90) instruction does far more than save power or yield to hyper‑threads—it controls speculation depth, avoids memory‑ordering penalties on lock release, and its behavior differs on x86 versus ARM, with misuse causing severe performance regressions in spin‑wait loops.
What the PAUSE instruction actually does
When developers write a spinlock, they often answer the question "what does pause do?" with "it saves power" or "it yields to hyper‑threads". Both are true side effects, but they miss the two primary hardware effects: it limits the speculative execution depth of an out‑of‑order core, and it prevents a pipeline‑clear penalty when the lock is finally released.
Raw spinlock implementation
A typical test‑and‑test‑and‑set (TTAS) spinlock in C++17 compiled with GCC 13 -O2 looks like this:
// C++17, GCC 13, x86‑64, -O2
void spin_lock(std::atomic<int>& l) {
for (;;) {
if (!l.exchange(1, std::memory_order_acquire))
return; // acquire lock
while (l.load(std::memory_order_relaxed) != 0) { }
}
}The inner while loop compiles to only three instructions on x86‑64:
.L3:
movl (%rdi), %eax # load lock byte
testl %eax, %eax
jne .L3 # still locked, loop againFor a superscalar, out‑of‑order core this is the ideal "speculation fuel": the branch predictor quickly learns that the branch always jumps back, the front‑end keeps fetching, decoding and issuing, and the load can be issued without waiting for the previous comparison.
Penalty on exiting the loop
When the holder writes 0 to release the lock, the store invalidates the cache line that the waiting core is still reading. Because x86‑TSO guarantees that loads appear in program order, the speculative loads that fetched the old value become illegal once the store retires. The hardware must therefore discard all in‑flight loads and the following instructions, flushing the pipeline. Intel’s SDM notes that exiting a spin loop can incur a "severe performance penalty" due to a possible memory‑order violation, counted by the machine_clears.memory_ordering perf event.
A single machine clear can cost dozens of cycles, and it happens exactly at the moment you most care about: the instant the lock is acquired.
How pause mitigates the penalty
The first thing pause does is break the speculative steady state. It tells the front‑end that the loop is a spin‑wait, causing the core to stop issuing new uops until the pause latency window expires. This de‑pipelines the pipeline, resets the speculation depth, and leaves only a couple of in‑flight loads when the lock is finally released, dramatically reducing the chance of a memory‑order violation. The SDM describes this as "avoids the memory order violation in most situations"—the benefit is probabilistic, not a full memory‑ordering barrier.
Impact on SMT (hyper‑threading)
On a physical core with two logical threads, the front‑end fetches for both threads in a round‑robin fashion, while resources such as the reservation stations and execution ports are shared. The three‑instruction spin loop can fully occupy the shared resources, starving the sibling thread. Inserting pause creates a window during which the spinning thread stops issuing, allowing the sibling thread to use the full bandwidth. Intel lengthened this window from ~10 cycles on pre‑Skylake CPUs to ~140 cycles on Skylake and later, explicitly to improve SMT resource hand‑off.
ARM equivalents: yield , isb , wfe
ARM does not have a single pause opcode. Instead it provides three mechanisms: yield – an SMT hint that tells the hardware the thread is low‑priority. On most server‑grade ARM cores (Neoverse N1/V1/V2, Apple silicon) which are single‑threaded, yield behaves as a NOP. isb – an instruction‑synchronization barrier. It forces a pipeline flush and serialises instruction fetch, providing a delay similar to pause but without any documented "pause" semantics. wfe – "wait for event". The core sleeps until one of four events (SEV broadcast, exclusive monitor clear, interrupt, or event stream) wakes it. Proper use requires a "check‑then‑arm‑monitor‑then‑sleep" protocol using sevl, ldxr, and wfe.
Linux kernel code wraps pause as rep; nop (opcode F3 90) for compatibility, and implements the ARM "check‑then‑sleep" sequence in __cmpwait with six instructions:
" sevl
"
" wfe
"
" ldxr %w[tmp], %[v]
"
" eor %w[tmp], %w[tmp], %w[val]
"
" cbnz %w[tmp], 1f
"
" wfe
"
"1:" // continueThis sequence ensures that a stale event is cleared before sleeping, avoiding the "sleep‑forever" risk.
Real‑world misuses and their consequences
On x86, many libraries (e.g., .NET's SpinWait) used a fixed count of pause instructions for exponential back‑off, assuming a constant ~4 ns latency on Haswell. Skylake increased the pause window to ~43 ns, turning the same code into a multi‑hundred‑millisecond stall for long spin periods, as documented in the "Why Skylake CPUs Are Sometimes 50% Slower" analysis.
Virtualisation adds another layer: x86 CPUs have PLE (Pause‑Loop Exiting) that triggers a VM‑exit after a configurable threshold, while ARM’s HCR_EL2.TWE causes a trap on wfe. Thus the same spin‑wait code can behave very differently in a guest.
Guidelines for writing spin‑wait loops
For user‑space code the best practice is to let the platform‑provided primitives handle the back‑off: C++20’s std::atomic::wait / notify_one combine a bounded spin with a futex. If you must write a custom loop, follow the three rules derived from the analysis:
On x86 use a single pause per iteration and calibrate the back‑off by measured time, not by instruction count.
On ARM either use isb as a placeholder delay (knowing it is just a pipeline flush) or implement the full sevl / ldxr / wfe protocol with proper monitor arming.
Never use yield on ARM unless you are targeting a core that actually implements SMT (e.g., Neoverse E1, Cortex‑A65); otherwise it is a pure NOP.
In kernel or runtime code where the wait time is bounded and far below a context‑switch, the above patterns give the most predictable performance.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
