logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·01.11.26
← Back to news

Register deprivation: spills and runtime under forced register scarcity

Original article
rjp.io|13 points|1 comments|by surprisetalk|Jul 30, 2026

Register Deprivation: Spills and Runtime under Forced Register Scarcity

Environment: Intel Xeon E-2236 @ 3.40GHz | Compiler: gcc 15.2.0 | Date: 2026-07-13

This study examines the impact of artificial register pressure. By utilizing the gcc -ffixed-reg flag, the researchers progressively reserved registers to shrink the available pool, subsequently measuring the resulting spill counts and execution times across nine distinct kernels.

TL;DR: Restricting register availability slowed 8 out of 9 kernels by between 14%14\%14% and 76%76\%76% at the most constrained budget. SipHash was the sole exception, showing no performance degradation.

The source code is available at https://github.com/rjpower/spillbench, with raw data stored in results.json.

Key Findings

  • Consistency: All results remained bit-identical regardless of the register budget; the reservation affected the instructions generated, not the mathematical outcome.
  • Correlation: There is a weak correlation between the number of additional spill instructions and the resulting slowdown (Pearson r=0.55r = 0.55r=0.55 across 105 data points).
  • Variability: The "cost" of a single spill varies wildly—from ≈0%/spill\approx 0\%/\text{spill}≈0%/spill (SipHash) to 2.2%/spill2.2\%/\text{spill}2.2%/spill (FIR filter), a difference of roughly 30×30\times30×.
  • Cross-File Impact:
    • For SHA-256, reserving XMM registers increased runtime by +5.6%+5.6\%+5.6%, while reserving GP registers caused a +33%+33\%+33% hit.
    • For Double Precision Matmul, reserving GP registers had no effect, but reserving XMM registers caused a +34%+34\%+34% slowdown.
    • Note: SHA-256 is an integer kernel, but because gcc auto-vectorizes parts of it using SSE at -O2, XMM reservation is not inert (spills rose from 8→658 \rightarrow 658→65).

Experimental Setup

The researchers tested two primary hypotheses:

  • H1: Reducing the register count will monotonically increase spills and decrease performance.
  • H2: Performance degradation will only occur when the register file actually utilized by the kernel (GP for integer, XMM for float) is restricted.

Methodology

The team used gcc -ffixed-reg to remove registers from the allocator's pool for the entire compilation process. Each kernel was recompiled for every budget increment.

GP Register Removal Order: r15 →\rightarrow→ r14 →\rightarrow→ r13 →\rightarrow→ r12 →\rightarrow→ rbp →\rightarrow→ rbx →\rightarrow→ r11 →\rightarrow→ r10 →\rightarrow→ r9 →\rightarrow→ r8 (Reducing the pool from 15 allocatable registers down to 5; rsp is always excluded).

Constraints: ABI registers (e.g., rax, rcx, rdx, rsi, rdi, and xmm0–3) were never reserved, as they are essential for argument passing and specific instructions like mul.

The Test Suite

Six kernels targeted the General Purpose (GP) file, and three targeted the XMM file.

KernelFileDescriptionValidation Method
ChaCha20GP20-round stream cipher (16×\times×32-bit state)RFC 8439 §2.3.2
SHA-256GPCompression function (8 vars + 64-word schedule)SHA-256("abc")
SipHash-2-4GPKeyed hash (4×\times×64-bit ARX)Reference vector
Integer matmulGP4×\times×4 register-blocked int64 micro-kernelvs. naive triple loop
LZ77 compressGPHash-chain match finderRound-trip (comp →\rightarrow→ decomp)
QuicksortGPIn-place recursive int32 sortOutput verification
Double matmulXMM4×\times×4 register-blocked f64 micro-kernelvs. naive triple loop
FIR filterXMM16-tap filter (8 output lanes)vs. naive tap sum
MandelbrotXMMEscape-time (few live doubles per pixel)Known escape counts

Measurement Process

  • Spill Metric: Static analysis via objdump counting instructions referencing a fixed stack slot (e.g., ...(%rsp)), excluding array indexing.
  • Timing: Performed on a single pinned core.
  • Hardware: Intel Xeon E-2236 @ 3.40 GHz, gcc 15.2.0, -O2 -fPIC -shared.
  • Constraint: Hardware performance counters were unavailable (perf_event_paranoid=4).

Result 1: Impact of Register Removal

Most kernels slowed down as registers were removed, though SipHash-2-4 remained unaffected.

Runtime vs Registers Graph Figure 2: Runtime trends for integer and float kernels.

Slowdown at Maximum Constraint:

KernelFileRuntime (Full →\rightarrow→ Min)SlowdownSpills (Full →\rightarrow→ Min)
FIR filterXMM135.5→238.9 ms135.5 \rightarrow 238.9\text{ ms}135.5→238.9 ms+76%+76\%+76%8→438 \rightarrow 438→43
Integer matmulGP140.5→199.7 ms140.5 \rightarrow 199.7\text{ ms}140.5→199.7 ms+42%+42\%+42%60→8260 \rightarrow 8260→82
Double matmulXMM131.5→176.4 ms131.5 \rightarrow 176.4\text{ ms}131.5→176.4 ms+34%+34\%+34%66→10066 \rightarrow 10066→100
SHA-256GP89.4→119.0 ms89.4 \rightarrow 119.0\text{ ms}89.4→119.0 ms+33%+33\%+33%8→808 \rightarrow 808→80
QuicksortGP134.2→171.7 ms134.2 \rightarrow 171.7\text{ ms}134.2→171.7 ms+28%+28\%+28%4→494 \rightarrow 494→49
ChaCha20GP88.5→110.0 ms88.5 \rightarrow 110.0\text{ ms}88.5→110.0 ms+24%+24\%+24%55→12155 \rightarrow 12155→121
LZ77 compressGP123.2→146.8 ms123.2 \rightarrow 146.8\text{ ms}123.2→146.8 ms+19%+19\%+19%39→11539 \rightarrow 11539→115
MandelbrotXMM101.6→116.2 ms101.6 \rightarrow 116.2\text{ ms}101.6→116.2 ms+14%+14\%+14%0→70 \rightarrow 70→7
SipHash-2-4GP79.4→78.1 ms79.4 \rightarrow 78.1\text{ ms}79.4→78.1 ms−2%-2\%−2%0→230 \rightarrow 230→23

Analysis of H1: H1 is fully supported partially supported. While the general direction is correct (fewer registers ≈\approx≈ slower code), the relationship is not strictly monotonic.

  • Spill anomalies: ChaCha20 and Quicksort both saw static spill counts drop after a register was removed in certain steps.
  • Runtime noise: Local dips occurred in LZ77 (5 steps), SipHash (3 steps), and Quicksort (3 steps), often within the ≈1%\approx 1\%≈1% measurement noise.
  • Initial State: Integer matmul began with 60 spills even with a full register file because its 4×44\times 44×4 block requires 16 int64 accumulators, exceeding the 15 available GP registers.

Result 2: Spill Count as a Predictor

The number of added spills is a poor indicator of actual performance loss.

Correlation Analysis: Across all 105 test points, the Pearson correlation coefficient is r=0.55r = 0.55r=0.55.

Cost Per Added Spill:

KernelAdded Spills (Min)Total SlowdownSlowdown per Spill
FIR filter+35+35+35+76%+76\%+76%2.18%/spill2.18\%/\text{spill}2.18%/spill
Mandelbrot+7+7+7+14%+14\%+14%2.05%/spill2.05\%/\text{spill}2.05%/spill
Integer matmul+22+22+22+42%+42\%+42%1.92%/spill1.92\%/\text{spill}1.92%/spill
Double matmul+34+34+34+34%+34\%+34%1.00%/spill1.00\%/\text{spill}1.00%/spill
Quicksort+45+45+45+28%+28\%+28%0.62%/spill0.62\%/\text{spill}0.62%/spill
SHA-256+72+72+72+33%+33\%+33%0.46%/spill0.46\%/\text{spill}0.46%/spill
ChaCha20+66+66+66+24%+24\%+24%0.37%/spill0.37\%/\text{spill}0.37%/spill
LZ77 compress+76+76+76+19%+19\%+19%0.25%/spill0.25\%/\text{spill}0.25%/spill
SipHash-2-4+23+23+23−2%-2\%−2%≈0%/spill\approx 0\%/\text{spill}≈0%/spill

Observation: The disparity is stark: Integer matmul suffered a +42%+42\%+42% slowdown with only 22 extra spills, whereas LZ77 added 76 spills but only slowed by +19%+19\%+19%.

Example of a spill instruction identified by objdump:

movq    %rax, -8(%rsp)  # Store register to stack
movq    -8(%rsp), %rdx  # Reload from stack

The researchers conclude that the explanation for this variance likely lies in... [Article ends here]