2026-09-01
The micro-op cache (Intel calls it the DSB, Decoded Stream Buffer) stores already-decoded uops so the CPU can skip the expensive x86 decode pipeline on hot code. But it has an obscure structural rule that bites real workloads: each uop cache line can hold at most one taken branch, and that branch must be the last uop in the line.
On Skylake-era and later Intel cores, a uop cache line holds up to 6 uops corresponding to a 32-byte window of x86 code. The line terminates early on any of these events:
The taken branch rule is the sneaky one. If your loop body contains a conditional jump that's usually taken, that jump ends the uop cache line — even if only 2 uops were packed into it. The remaining 4 uop slots are wasted. Worse, if you have branch-heavy code (say, a chain of predicted-taken branches every few instructions), you might pack only 1–2 uops per line, blowing through the uop cache's capacity 3× faster than expected.
Concrete example: A tight interpreter dispatch loop with a computed-goto pattern (indirect branch every ~4 instructions). Naively you'd expect the loop to fit comfortably in the ~1500-uop DSB. But because every dispatch is a taken indirect branch, each uop cache line holds only ~3 uops instead of 6. Effective capacity halves, and once you exceed it, execution falls back to the legacy decode path — costing you the classic 4-wide decoder throughput cap versus 6-wide DSB delivery.
Rule of thumb: For hot loops, compute uop cache footprint as:
lines_used ≈ ceil(uops_per_iteration / min(6, uops_between_taken_branches))
If your loop has a taken branch every 2 uops, your effective density is 2 uops/line, so a 30-uop loop consumes 15 cache lines — not 5. At 8 lines per 32-byte region and limited sets, you can thrash the DSB with what looks like tiny code.
Mitigation: Straighten hot paths so the predicted-taken direction is the fall-through (invert branch conditions), unroll loops to amortize the terminating backward branch across more uops, and avoid clustering multiple taken branches within a 32-byte window.
