advanced26 min studyLesson 463 in Engineering

Register pressure and spilling

Account for rounded register allocation, distinguish local memory from on-chip storage, and optimize measured work rather than occupancy alone.

On this page

What you’ll understand

  • Explain why a source variable does not guarantee one register.
  • Compute a scoped rounded-register budget.
  • Distinguish residency, spilling, and useful kernel performance.

A fused tensor kernel removes intermediate global writes but keeps many accumulators live. Register demand rises, fewer warps may reside on an SM, and some values may spill. Those effects interact rather than forming one monotonic rule. This lesson uses CUDA 12.8.1 documentation and explicitly scoped allocation arithmetic; register-file sizes, allocation units, and residency limits must be checked for the compiled target rather than copied across GPU generations.

The compiler allocates live values

Register demand depends on live ranges, unrolling, inlining, indexing, and generated instructions, not simply the number of variables declared in source. Large or dynamically indexed thread-private arrays may reside in local memory even without a simple register-exhaustion story. Inspect compilation resource reports and final generated code for the actual architecture. PTX is an intermediate representation, so later compilation can change placement. A favorable source rewrite can unexpectedly add registers through temporaries or a different instruction selection.

CUDA local memory is private in its address-space semantics but backed by device memory, not a private on-chip register extension. Spilled accesses can therefore add traffic and latency, subject to caching and their access pattern. Threads accessing corresponding local offsets can have coalesced accesses, but that does not make spills free. Do not confuse local memory with block-shared memory, whose ownership, lifetime, and synchronization rules are different. The word local describes visibility here, not physical proximity.

Rwarp=G⌈32rG⌉R_{\mathrm{warp}}=G\left\lceil\frac{32r}{G}\right\rceil
For an assumed per-warp allocation unit G registers and reported r registers per thread, this rounded model gives allocated 32-bit registers per warp. Actual target allocation rules and block granularity must be verified; it is not a universal occupancy formula.↔ Scroll wide equations horizontally.

Rounding creates resource thresholds

A small register increase can cross an allocation boundary and reduce resident blocks abruptly. Other limits, including threads, warps, shared memory, and architectural block caps, may bind first. Thus dividing total registers by threads and a reported per-thread count is not a complete occupancy calculation. CUDA’s occupancy APIs and profiling tools can account for target-specific constraints. State which bottleneck your arithmetic includes and which it deliberately omits before reporting a resident-block prediction.

The CUDA 12.8.1 best-practices guide gives a Volta compute-capability 7.0 example with 256-register allocation granularity per warp. Use that scoped rule to understand rounding rather than assuming every newer target shares every detail. Reducing a compiler register cap may raise nominal residency while forcing more spills or instructions. Conversely, a lower-occupancy kernel can still perform well when it has sufficient independent work and reuse. Compare execution time, memory traffic, and numerical behavior after each meaningful change.

Nblocks,reg≤⌊RSMwRwarp⌋N_{\mathrm{blocks,reg}}\leq\left\lfloor\frac{R_{\mathrm{SM}}}{wR_{\mathrm{warp}}}\right\rfloor
With w full warps per block and R_SM registers in one SM, this is a register-only resident-block bound under the assumed allocation model. Other residency constraints and special allocation rules can lower the result.↔ Scroll wide equations horizontally.

Optimize the full data path

Tune fusion, tile dimensions, or unrolling based on measured bottlenecks. Shorter live ranges can reduce pressure without reducing useful parallelism, while storing values in shared memory trades registers for another limited resource and introduces access and synchronization costs. A kernel that passes a small benchmark can spill heavily for another template specialization or input shape. Record compile options, target architecture, launch size, and specialization alongside resource reports so regressions can be traced to the code actually running.

The Roofline lab provides only a traffic analogy. Reset, set intensity to 8 FLOP per byte and bandwidth to 80 GB/s, predict the synthetic ceiling, then Step. Compare a fresh Reset with both intensity at 4 and bandwidth at 80, interpreting the changed intensity as an assumed increase in bottleneck traffic. Its compute ceiling remains 1000 GFLOP/s. It has no registers, warps, occupancy, or spill compiler; explain why a lower traffic ceiling cannot determine whether a real register-cap change is beneficial.

Round one warp’s register demand

Use a teaching allocation model with G = 256 32-bit registers per warp, eight full warps per block, and R_SM = 65,536. Compare reported r = 32 and r = 33. Treat the result as register-only accounting, not a full target occupancy prediction.

  1. At r = 32, a warp requests 1,024 registers, already four allocation units. Eight warps consume 8,192 per block, and the register-only bound is floor(65,536/8,192) = 8 blocks.

  2. At r = 33, raw demand is 1,056 per warp, rounded to 1,280. Eight warps consume 10,240 per block, giving floor(65,536/10,240) = 6 blocks.

  3. The rounded budget changes discontinuously. Check actual architecture rules, other block limits, and whether the compiler introduces spills before interpreting either count as achieved residency or performance.

The result

One extra reported register per thread can cross an allocation threshold. It does not imply a proportional one-register change in the block’s allocated storage.

Explore the model

Interactive lab

Interactive lab · roofline

Spill traffic changes an assumed intensity

Reset, set intensity I = 8 and bandwidth B = 80, predict the ceiling, then Step. Compare a fresh Reset with both I = 4 and B = 80. The fixed 1000-GFLOP/s Roofline is synthetic and contains no register allocation or occupancy model.

Spill traffic changes an assumed intensitySynthetic roofline ceiling min(1000, B·I), in GFLOP/s, with B in decimal GB/s and I in FLOP/byte. Playback traces the roof without changing the selected kernel. This upper-bound model is not a measured throughput prediction, hardware comparison or price model. Current readings: Attainable ceiling: 640 GFLOP/s; Ridge intensity: 12.5 FLOP/byte; Ceiling regime: Memory-bound ceiling. All parameters are adjustable with the labeled controls below.Hypothetical roofline upper bound: 640 GFLOP per second at operational intensity 8 FLOP per byte and bandwidth 80 gigabytes per second.0.1110641101001000Operational intensity · FLOP/byte · log scaleCeiling · GFLOP/s · log scalePeak: 1000
Attainable ceiling / kernelBandwidth roof / traceCompute ceiling
Attainable ceiling
640 GFLOP/s
Ridge intensity
12.5 FLOP/byte
Ceiling regime
Memory-bound ceiling
t = 0 s

Synthetic roofline ceiling min(1000, B·I), in GFLOP/s, with B in decimal GB/s and I in FLOP/byte. Playback traces the roof without changing the selected kernel. This upper-bound model is not a measured throughput prediction, hardware comparison or price model. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.

Look forTraffic is one consequence to measure; a register or occupancy count alone does not predict useful kernel speed.

Check your understanding

A compiler option lowers reported register usage and raises a register-only residency bound, but adds many local-memory loads. Which conclusion is justified?

Try it yourself

Under the same assumed G = 256 model, r = 40 and four warps per block, calculate registers per warp and per block. With 65,536 registers, find the register-only block bound and name one omitted constraint.

Take a moment to make a prediction or work it out on paper. Open a hint when you need one.

Give me a hint

Forty times thirty-two is already a multiple of 256.

Compare with the solution

Each warp uses 1,280 registers and each four-warp block uses 5,120. The register-only bound is floor(65,536/5,120) = 12 blocks. Thread or warp limits, shared-memory allocation, architectural block limits, and target-specific rules can reduce actual residency. The bound does not predict execution time or spill behavior.

Take it with you

  • Live ranges and compilation determine register placement and spills.
  • Allocation rounding and several hardware limits shape residency.
  • Local memory is device-memory-backed; optimize measured work rather than occupancy alone.

Read further

Original sources and deeper explanations to follow at your own pace.

Optional progress, saved in this browser when available. Revisit any lesson at any time.

Lesson not marked complete.