02.01 · Concept · Free
Why the GPU Waits
Understand the GPU as a throughput machine working against a latency-optimized CPU. Explain the three performance regimes: compute bound, memory bandwidth bound, and overhead bound.
Curated for this lesson
The Hardware Floor
Why the GPU Waits
This section breaks GPU time into 'fixed costs for kernel launches,' 'memory accesses,' and 'real computation,' then uses bandwidth and the roofline model to explain whether a kernel is 'compute bound or memory bound.'
The GPU waits when its many lanes are starved by compute, memory, or host-side overhead. LLM inference alternates between regimes: prefill can keep arithmetic units busy, while small-batch decode often streams weights and cache data faster than it can reuse them. Batching, fusion, paged cache, and better scheduling help by moving the bottleneck.
What this lesson answers
- why does the GPU wait during inference
- compute bound versus memory bandwidth bound GPU kernels
- why batching improves LLM decode throughput
Notes
The hardware floor for LLM inference is the minimum time imposed by the slower of three resources: floating-point throughput, memory bandwidth, and launch/scheduling overhead. In roofline form the per-token lower bound is , where is FLOPs issued for the token, is effective GPU compute rate, is bytes moved from HBM, and is HBM bandwidth. The GPU is a throughput machine: it wants thousands of independent lanes kept busy with long streams of arithmetic or memory transactions. The CPU is latency-optimized: it is good at branching, scheduling, and initiating small tasks, but it cannot feed the GPU one tiny matrix-vector operation at a time without leaving gaps. “Why the GPU waits” usually means one of three concrete stalls: Tensor Cores have no ready tiles, HBM transactions are the limiter, or the kernel stream is fragmented by CPU/framework overhead.
In prefill, a transformer looks like large matrix-matrix multiplication, so arithmetic intensity is high and the GPU can approach the compute roof. In decode for batch size 1, each new token is closer to matrix-vector multiplication over all weights, so the same weights are reread for very little reuse. A 70B dense model in FP16 has roughly GB of weights. On an 80GB H100 with TB/s, even ignoring KV reads and activations, one forward token cannot take less than s, or about tokens/s, if the weights must stream from HBM. The FLOP count is about GFLOP per token for B parameters; even at a conservative effective TFLOP/s this is ms, far below the ms bandwidth floor. That is the memory-bandwidth-bound regime: Tensor Cores are mostly waiting for weights, not multiplying.
Batching changes the arithmetic intensity because the same streamed weights serve multiple sequences. If batch decode tokens are multiplied by the same 70B weights, the traffic is still about GB for weights, but the useful arithmetic rises to GFLOP TFLOP, raising intensity from roughly FLOP/byte to FLOP/byte. The bandwidth floor remains ms for the whole batch, which is tokens/s aggregate before KV and overhead; the compute time at TFLOP/s is ms, still below bandwidth. Around inverted through intensity, the bottleneck can move toward compute, and for very large prefill matrices FlashAttention from Dao et al., “FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness” and FlashAttention-2 reduce HBM traffic enough that attention kernels become compute-competitive rather than pure memory traffic.
The overhead-bound regime is different: the GPU waits because the host and runtime are feeding it kernels, copies, synchronization points, and request decisions too slowly. A single decode step may involve attention, MLP projections, elementwise ops, sampling, logits processing, cache address lookup, and scheduler bookkeeping; if these are many small kernels at low batch, tens of microseconds of launch and dependency latency can dominate useful work. vLLM’s PagedAttention, from Kwon et al. “Efficient Memory Management for Large Language Model Serving with PagedAttention” and the vLLM 2023 release, attacks this by paging KV cache blocks so continuous batching can keep more sequences resident without huge contiguous allocations. SGLang’s RadixAttention and runtime, TensorRT-LLM’s inflight batching and fused kernels, NVIDIA Dynamo’s disaggregated serving stack, and llm-d’s Kubernetes-native serving architecture all exist because the hardware floor is not just a CUDA kernel; it includes request admission, prefix reuse, KV placement, and inter-GPU routing.
The KV cache makes the bandwidth floor token-position dependent. With grouped-query attention using KV heads and head dimension in FP16, each layer stores per token bytes for keys and values. For an -layer 70B-style model, that is bytes, or about KiB per cached token per sequence. Attending over a -token context therefore reads on the order of GB of KV data per generated token if implemented naively across layers. At TB/s that alone is ms, much less than the ms weight-streaming floor at batch 1 but material at high batch or with quantized weights. PagedAttention improves allocation and batching, FlashAttention improves IO within attention, and TensorRT-LLM-style paged KV plus fused attention reduce waste, but none abolishes the fact that longer context turns decode into a KV bandwidth problem.
The techniques stop working when they move the bottleneck instead of removing it. Continuous batching helps until latency SLOs are violated, the batch becomes compute-bound, or KV cache capacity forces eviction, recomputation, or cross-device traffic. PagedAttention can make things worse for short, uniform prompts because page-table indirection and non-contiguous block gathers add overhead without solving fragmentation. FlashAttention helps when attention IO dominates, but for batch-1 decode of a huge dense model the weight matrix traffic dominates, so an optimized attention kernel barely moves tokens/s. Speculative decoding with EAGLE, from Li et al. “EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty,” can reduce target-model steps when draft acceptance is high, but it hurts when the draft model is poorly matched, sampling temperature lowers acceptance, or verification batches are too small to amortize extra kernels. Dynamo, llm-d, vLLM, SGLang, and TensorRT-LLM expose the same engineering truth: once compute, HBM, or overhead is saturated, another layer of scheduling only reshuffles the wait.
Common questions
- What does it mean for an inference kernel to be compute bound?
- A kernel is compute bound when arithmetic throughput is the limiting resource. The GPU has enough data ready, memory is not the main constraint, and further speedup depends on doing fewer operations or using the arithmetic units more efficiently. Large prefill matrix multiplications often behave this way because each loaded value is reused heavily.
- Why is LLM decode often limited by memory bandwidth?
- Decode at low batch reuses model weights poorly. Each generated token may require reading large weight matrices and parts of the KV cache, but performs relatively little arithmetic per byte loaded. In that regime the Tensor Cores are not the scarce resource; the limiting factor is how quickly HBM can supply data.
- How can runtime overhead make a GPU idle?
- The GPU can sit idle when work arrives as many small kernels, synchronisations, copies, and scheduling decisions. The CPU and framework may be busy managing requests, cache addresses, sampling, and launches while the GPU has no large ready stream of work. Kernel fusion, continuous batching, and better cache management reduce this fragmentation.
