6.2.10GPU Architecture

Occupancy and latency hiding

2,977 words14 min readdifficulty · medium2 backlinks

What is Occupancy?

WHY does occupancy matter?

  • Memory reads from DRAM take ~400-800 cycles
  • Arithmetic operations take ~4-10 cycles (even with dependencies)
  • If you have only 1 warp running, the SM stalls during these waits → wasted cycles
  • With 32+ warps, the scheduler switches to a ready warp every cycle → no stalls

HOW does the GPU achieve this?

  1. Context is free: Each warp's registers stay on-chip. Switching costs ~0 cycles.
  2. Warp scheduler: Picks a ready warp (no data dependency, no memory wait) each cycle.
  3. Overlapping execution: While Warp A waits for ld.global, Warps B, C, D compute.

Latency Hiding: The Math

Derivation from First Principles:

Suppose a memory operation takes L=400L = 400 cycles. An SM can issue 1 warp-instruction per cycle (for a single scheduler; modern SMs have 4 schedulers, but let's start simple).

  • Goal: Keep the pipeline full—issue 1 instruction every cycle.
  • Problem: A warp that issues a memory load must wait LL cycles before its result is ready.
  • Solution: Have LL other warp-instructions ready to issue during that wait.

Thus: Warps neededLInstructions per Warp per Cycle\text{Warps needed} \geq \frac{L}{\text{Instructions per Warp per Cycle}}

If each warp issues 1 instruction/cycle when scheduled: WarpsL\text{Warps} \geq L

For L=400L = 400 cycles → need 400 warp-instructions in flight to fully hide latency with 1 scheduler. Modern SMs with 4 schedulers and pipelined execution supply much of this concurrency per cycle. In practice, 16-32 active warps suffice for most kernels due to:

  • Multiple instructions in flight per warp (ILP)
  • Mix of compute and memory ops
  • 4 schedulers issuing from different warps in consecutive cycles (SM-wide MLP)

WHY 32 threads per warp? Historical: SIMD width balances hardware complexity vs. paralelism. 32 lanes means 32-wide vector ALUs, which fit transistor budgets and memory coalescing patterns (cache lines are 128B = 32× 4-byte floats).


Factors Limiting Occupancy

Example (A100 GPU):

  • Max warps/SM: 64 (2048 threads)
  • Register file: 65,536 registers/SM
  • Shared memory: 164 KB/SM (or 100 KB with L1 cache enabled)
  • Max blocks/SM: 32

Scenario 1: Kernel uses 64 registers/thread, no shared memory.

  • Registers per warp: 64×32=204864 \times 32 = 2048
  • Warps possible: 65536/2048=32\lfloor 65536 / 2048 \rfloor = 32 warps
  • Occupancy: 32/64 = 50%

Scenario 2: Kernel uses 32 registers/thread, 48 KB shared mem/block, 256 threads/block (8 warps).

  • Register limit: 65536/(32×32)=64\lfloor 65536 / (32 \times 32) \rfloor = 64 warps ✓
  • Shared mem limit: 164/48=3\lfloor 164 / 48 \rfloor = 3 blocks → 3×8=243 \times 8 = 24 warps
  • Occupancy: 24/64 = 37.5%

WHY does this matter? Lower occupancy → fewer warps to hide latency → more stalls → lower throughput.


Examples with Step-by-Step Reasoning


Common Mistakes


Measuring and Optimizing

Tools:

  1. CUDA Occupancy API: cudaOccupancyMaxActiveBlocksPerMultiprocessor()
  2. Nsight Compute: Shows achieved occupancy, stall reasons (memory, ALU, sync).
  3. Heuristic: Run kernel with different block sizes (128, 256, 512 threads). Plot performance vs. theoretical occupancy.

Optimization Strategies:

Limiter Fix Trade-off
Registers Reduce live variables, use __restrict__, compiler flag May increase computation
Shared Memory Tile smaller, use L1 cache (cudaFuncSetCacheConfig) Less data reuse
Block Size Increase threads/block (256→512) Less flexibility in grid dim
Synchronization Minimize __syncthreads(), use warp-level primitives Code complexity
Divergence Avoid branching; use predication or split kernels More instructions

Example: If Nsight shows "Warp was stalled waiting for memory," but occupancy is 30%, try:

  1. Increase block size to 512 threads → 50% occupancy.
  2. If registers limit, reduce precision (FP32 → FP16 where safe).
  3. If still stalling, the kernel is fundamentally memory-bound—optimize memory access patterns (coalescing, prefetching).

Active Recall

Recall Explain to a 12-Year-Old

Imagine you're doing homework. Each question takes you 2 minutes to read, 10 seconds to solve. If you only have 1 question, you sit idle for 2 minutes waiting for it. Boring!

But if you have 12 questions, you read Question 1, then while the book "loads" Question 2, you solve Question 1. You never wait—you're always working on something. (Reading Question 1 still takes the full 2 minutes—you just fill that time with other work, you don't make the book load faster.)

A GPU is like having 1000 questions and 1000 brains. Each brain (thread) grabs a question. While one brain waits for the "book" (memory) to load, 999 others are solving. By the time you cycle back, the first brain's book is ready.

Occupancy is: "How many of your brains are busy?" If 500 out of 1000 brains have questions, occupancy is 50%. The more busy brains, the less waiting.



Connections

  • 6.2.1-SM-Architecture – Occupancy depends on SM resources (regs, shared mem, warp slots)
  • 6.2.8-Warp-Scheduling – The scheduler that switches between warps to hide latency
  • 6.2.9-Memory-Coalescing – High occupancy amplifies the cost of uncoalesced access
  • 6.2.11-Tensor-Cores – Tensor Core ops have fixed warp counts; occupancy less critical
  • 7.1.3-Roofline-Model – Occupancy determines if you hit compute or memory ceiling
  • 5.3.4-Littles-Law – Theoretical foundation for latency hiding via concurrency

Flashcards

#flashcards/hardware

What is GPU occupancy?
The ratio of active warps to maximum possible warps per SM, usually expressed as a percentage (e.g., 32/64 = 50%).
Why does high occupancy hide latency?
With many active warps, the warp scheduler can always find a ready warp to execute while others wait for memory or ALU results, eliminating stalls.
What are the three main factors limiting occupancy?
(1) Registers per thread, (2) Shared memory per block, (3) Maximum blocks per SM. Occupancy is the minimum of these constraints.
Does adding warps reduce the latency of a single memory load?
No. Concurrency (Little's Law) lets you overlap many loads so the SM never idles, but the intrinsic ~400-cycle latency of each load is unchanged.
Derive the latency hiding requirement from Little's Law
To hide latency LL cycles at throughput TT ops/cycle, need L×TL \times T operations in flight. For 400-cycle memory latency and 1 warp-instruction/cycle, need ~400 warp-instructions outstanding (but ILP + SM-wide MLP + 4 schedulers reduce distinct warps to 16-32).
Why is 100% occupancy not always optimal?
Beyond ~50%, gains plateau. High occupancy can increase cache pressure and register spills, hurting performance. The goal is zero stalls, not maximum threads.
What is the A100's maximum occupancy in warps per SM?
64 warps/SM (2048 threads, since each warp = 32 threads).
If a kernel uses 80 registers/thread on an A100 (65,536 regs/SM), what's the occupancy?
65536/(80×32)=25\lfloor 65536 / (80 \times 32) \rfloor = 25 warps → Occupancy = 25/64 ≈ 39%.
How many outstanding memory requests does a single warp sustain?
One memory instruction at a time (which may split into a few transactions if uncoalesced). Concurrency comes SM-wide across many warps, not from one warp issuing 32 independent requests.
Do global stores add their full latency to the critical path?
No. Global stores are buffered and retire asynchronously (drain in the background), so you normally don't add store latency to critical-path memory time.
When does low occupancy NOT hurt performance?
When the kernel is compute-bound with high instruction-level parallelism (ILP), or when achieved throughput already saturates the hardware despite fewer active warps.

Concept Map

ratio of

over

causes

high occupancy enables

prevents

switches to ready warp

enables

Threads = L x Throughput

derived from

supply

so 16-32 warps suffice

Occupancy

Active Warps per SM

Max Warps per SM

Memory Latency ~400-800 cyc

SM Stalls if few warps

Latency Hiding

Warp Scheduler

Free Context Switch

Little's Law

Concurrency Required

4 Schedulers plus ILP

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, GPU ka core magic ye hai ki wo slow memory ka wait nahi karta. Jab ek warp (32 threads ka group) memory se data lane ke liye ruka hota hai—jo ki 400-800 cycles le sakta hai—tab GPU dusre ready warps ko chalata rehta hai. Isko bolte hain latency hiding. Aur occupancy simply ye batata hai ki tum apne SM (Streaming Multiprocessor) ki capacity ka kitna hissa use kar rahe ho—yaani active warps divided by maximum possible warps. Restaurant kitchen wali example yaad rakho: agar tumhare paas 100 orders hain to har chef busy rehta hai, koi idle nahi khada, aur ingredients ka wait ho bhi to kaam ruकता nahi.

Ab ye matter kyun karta hai? Kyunki context switching GPU mein bilkul free hai—har warp ke registers on-chip padhe rehte hain, to warp badalne mein zero cycles lagte hain. Warp scheduler har cycle ek aisa warp uthata hai jo ready ho (jiska data aa gaya ho ya jo waiting nahi hai). Isliye agar tumhare paas kaafi warps hain, to SM kabhi stall nahi hoti aur pipeline hamesha bhari rehti hai. Little's Law bhi yahi kehta hai: latency LL ko hide karne ke liye tumhe L×L \times throughput jitni concurrency chahiye. Practically 16-32 active warps zyadatar kernels ke liye enough hote hain, kyunki multiple instructions per warp (ILP) aur 4 schedulers milkar concurrency provide kar dete hain.

Lekin ek important cheez samajhna—occupancy tumhare registers per thread, shared memory per block, aur blocks per SM jaisi resources se limit hoti hai. Agar tumhara kernel bahut zyada registers use karta hai, to kam warps fit honge aur occupancy gir jayegi. Isliye final occupancy in saare constraints ka minimum hoti hai. Yaad rakho, zyada concurrency single operation ki latency ko chhota nahi karti—wo bas overlap karne deti hai taaki SM idle na baithe. Ye samajhna zaroori hai kyunki jab tum performance tune karoge, to tumhe balance karna padega ki registers aur shared memory kitna use karein taaki enough warps chalein aur latency effectively hide ho sake.

Go deeper — visual, from zero

Test yourself — GPU Architecture

Connections