← Glossary
CUDA glossaryMemory
CC 7.5

What are registers in CUDA?

The fastest storage on the chip, private to one thread, handed out by the compiler and capped by a fixed pool each SM divides among its resident threads.

A register holds 32 bits with one thread's name on it. You never declare one and you cannot take its address. float carry = 0.0f; in a kernel body is a register unless the compiler decides otherwise, and that decision belongs to ptxas, which allocates them during the build and prints the count when you ask for it. One rule explains most of what follows: no instruction reads register number k for a value of k known only at run time. A scalar always fits. An array indexed by a variable never does, and where it goes instead is local memory, which is DRAM wearing a friendly name.

The count is charged per resident thread, not per launch, because every thread the SM is holding keeps its own copy for as long as its block sits there. A Tesla T4 has 65,536 32-bit registers on each SM and room for 1,024 threads, so 64 per thread is the most a kernel can ask for and still fill the card. Ask for the 65th and threads have to leave. Blocks are the unit the pool is spent in rather than threads, which is why the loss arrives in chunks: at 256 threads a block a 72-register kernel needs 18,432 registers per block, three of those fit inside 65,536, and 768 of the 1,024 slots get used.

The wrong lesson to draw is that fewer registers is better. Registers are where a thread does its arithmetic, so spending fewer means either doing less work per thread or putting values somewhere slower. Day 17 caps one kernel at 64 with __launch_bounds__, gets its occupancy back to 100 percent, and watches it get slower, because the values that stopped fitting went to memory. The target is not a low number. It is whatever register pressure says it should be for that kernel, and the report plus a clock is how you find it.

Measured

On a Tesla T4, driver 595.84, CUDA 12.6 (V12.6.85), built with nvcc -O3 -arch=sm_75, day 17 compiled four kernels from one file and read every register count back off the device with cudaFuncGetAttributes, so the compiler's report and the runtime's view had to agree.

Kernel Registers per thread Occupancy Time
decayNarrow, 4 taps 10 100.0 % 0.038 ms
decayWide, 64 taps 72 75.0 % 0.164 ms

Both ran at 256 threads a block over the same input. The narrow kernel does a fraction of the arithmetic, so the two times are not a race and nothing here says 10 registers beats 72. What the pair does settle is where the ceiling sits: 72 is above the T4's 64 and it costs a quarter of the SM's thread slots, and neither number appears anywhere in the source. The other two kernels in that build put values in local memory, and their rows are on day 17.

Diagram

occupancy-stepper, set to the register axis. One SM as a 1,024-slot grid with a register budget bar beside it. At 10 registers a thread the grid fills; at 64 it still fills and the budget bar reads full; at 72 the bar overflows on the fourth block and 256 slots grey out.

Alt text: "One SM's 65,536 registers against its 1,024 thread slots. At 64 registers a thread every slot is used. At 72 only three 256-thread blocks fit and a quarter of the slots stay empty."

Related terms

Where you meet this

Sources

Byline

Written by: pending. Reviewed by: pending. Written on: pending. Last checked on: pending. This entry stays a draft until a named author and a different named reviewer sign it.