Skip to content
The Batch

04.01 · Concept · Free

One Request at a Time

Explain why batch size 1 is the worst case for a GPU: the whole weight matrix is read from HBM to produce a single token, so arithmetic intensity collapses and the device runs at a few percent of peak.

This video is hosted by Klay so it plays even when YouTube blocks embedding.

Curated for this lesson

The Batch

One Request at a Time

The speaker explains arithmetic intensity and memory bounds, showing that a matrix-vector product must 'read W' of size '2N squared' for only about 'N dot products' so it is 'also memory bound,' then contrasts matrix multiplication where large matrices saturate GPUs and notes that 'matrix vector products is essentially kind of what goes on when you're doing transformer inference' because tokens are generated one at a time.

Batch size 1 turns transformer decode into weight streaming: each layer reads its large matrices from HBM to compute one token row, so there is almost no reuse. The GPU becomes limited by memory bandwidth rather than tensor cores, and batching improves throughput by applying the same weights to many active tokens.

What this lesson answers

  • why is batch size 1 bad for GPU inference
  • how does batching improve transformer decode throughput
  • why is single token decode memory bound

Notes

Batching in decoder-only inference is the mechanism of applying the same layer weights to multiple independent token positions before those weights are evicted from the GPU memory hierarchy. For a linear projection , with and , the per-token batch is rows, and the arithmetic intensity is approximately FLOP/byte, where is bytes per element and the dominant term at small is the weight read . When , this reduces to roughly , or about FLOP/byte for FP16, because every parameter is streamed from HBM to create one token’s activations. The GPU is then not “doing one user quickly”; it is repeatedly launching enormous matrix-vector products whose reuse factor on is one. Continuous batching in Orca, later industrialized in vLLM’s PagedAttention paper and systems such as SGLang, TensorRT-LLM, NVIDIA Dynamo, and llm-d, is specifically about raising this reuse factor during decode.

A concrete H100 calculation makes the failure mode visible. Take a 70B-parameter decoder in FP16, so the weights are about GB. On an 80GB H100 with HBM3 bandwidth around TB/s, a single-token decode that must stream the full model has a lower-bound weight-read time of s, or about tokens/s, before KV-cache traffic, collectives, launch overheads, and non-GEMM work. The math for that token is about B GFLOP, so at tok/s the chip is doing roughly TFLOP/s, only about of a TFLOP/s FP16 tensor-core peak H100. This is not because the model is too small for the GPU; it is because GFLOP GB FLOP/byte, while the H100 roofline knee is around FLOP/byte. Batch size 1 is hundreds of times too memory-bound to approach tensor-core peak.

Increasing decode batch changes the same calculation because the weight bytes are amortized across tokens. Ignoring activation and KV traffic, a batch decode of the same 70B model reads about the same GB of weights but performs GFLOP TFLOP, so FLOP/byte. That is still below the H100 knee, but it is sixty-four times better than batch 1 and can move the realized throughput from single-digit TFLOP/s into the hundreds once kernels become large GEMMs rather than GEMVs. The limiting per-token weight-bandwidth cost becomes GB, implying an optimistic tok/s if nothing else bottlenecks. This is the reason serving stacks do not reserve a GPU for one request unless latency isolation dominates economics: the exact same parameter stream can produce many next-token logits. FlashAttention helps the attention part by tiling QK/V through SRAM, but during decode the large MLP and projection weights still demand cross-request batching for reuse.

The implementation detail is that requests do not naturally stay aligned: one prompt is prefilled, another is decoding token 37, another stops on EOS. Orca’s “iteration-level scheduling” paper made the key observation that the batch should be rebuilt every decode step, not only at request boundaries. vLLM’s 2023 PagedAttention paper then made this practical by paging the KV cache so a changing logical batch can reference non-contiguous KV blocks without copying long sequences. SGLang extends this style with a runtime for structured generation and RadixAttention-style prefix reuse; TensorRT-LLM exposes in-flight batching and paged KV kernels in NVIDIA’s optimized engine; NVIDIA Dynamo and llm-d package disaggregated prefill/decode and distributed serving around the same need to keep decode batches dense. EAGLE, from the speculative decoding paper of that name, attacks a different term: it drafts multiple candidate tokens so one expensive target-model pass may accept several tokens, increasing useful work per weight stream when the verifier batch would otherwise be thin.

There are concrete regimes where batching stops helping or hurts. First, prefill for long prompts is already a matrix-matrix or large attention workload: a single request with prompt tokens has positions through the weights, so adding unrelated prefills may only increase queueing delay or push attention into KV-memory pressure. Second, decode batching beyond the point where KV-cache reads, logits processing, all-reduce, or scheduler overhead dominate will not improve tokens/s linearly; with grouped-query attention using KV heads and head dimension in FP16, each layer reads roughly bytes of K and V per decoded token. At and layers, that is GB/token of KV traffic, comparable to the GB/token amortized weight traffic at . Third, batching worsens time-to-first-token and per-request tail latency when arrivals are sparse or service-level objectives are strict, because the scheduler waits or interleaves more work before a user’s next token.

Batch size 1 is therefore the pathological decode case for the GPU execution model: almost every layer degenerates to a GEMV-shaped operation, the HBM stream of model weights is consumed once, and tensor cores sit underfed. The important distinction is not “one request” versus “many users” at the API layer, but “one token row” versus “many token rows” at each weight matrix. PagedAttention, FlashAttention, and TensorRT-LLM kernels reduce avoidable memory movement inside attention and KV management; vLLM, SGLang, Dynamo, and llm-d keep the live token rows numerous enough that the same weights are reused; EAGLE manufactures additional verified progress per expensive model invocation. When the active decode set is small, the roofline arithmetic is unforgiving: collapses toward FLOP/byte for FP16 weights, so an H100-class accelerator behaves like a very expensive memory streaming device rather than a near-petaflop matrix engine.

Common questions

Why does a single request underuse a GPU during decode?
During decode, a transformer usually produces one next token at a time. With only one active token row, each projection becomes shaped like a matrix-vector operation. The model weights still have to be read, but they are used once before being evicted, so memory bandwidth becomes the bottleneck and tensor cores wait for data.
How does batching make transformer inference faster?
Batching lets the serving system apply the same layer weights to multiple independent token positions while those weights are still useful in the GPU memory hierarchy. That changes the work from many thin matrix-vector operations towards larger matrix multiplications, increasing arithmetic intensity and making each byte of weight data produce more computation.
Does batching always help latency and throughput?
Batching helps most when decode is thin and weight reads dominate. It can hurt time-to-first-token or tail latency if the scheduler waits for more work, and it stops scaling cleanly once KV-cache reads, attention work, collectives, logits processing, or scheduling overhead become the main limit.