Batching is free throughput until it isn’t, the crossover from memory-bound to compute-bound tells you exactly where “free” ends.
The two regimes
= total bytes of modal weights = params x bytes per param B = batch size (number of sequences decoding concurrently in this step) S = sequence length; tokens of context per sequence (how much KV each one has accumulated) = KV bytes per token per sequence
= total KV cache bytes that must be read this step Add W for the # of bytes moved per decode step.
= achieved HBM bandwidth
= param count 2N flops per token 2NB flops per step (assuming dense matmuls) = achieved compute throughput for the relevant dtype.
- Small (): TPOT is flat, throughput grows linearly. Batching is nearly free.
- Mid : KV traffic starts to matter; throughput bends over toward its ceiling .
- Large : compute-bound. Throughput is pinned at and TPOT grows linearly. Pure latency loss, zero throughput gain.
The crossover batch
A decode GEMM does FLOPs while moving weight bytes. Arithmetic intensity . Set it equal to the device ops:byte ratio : The precision cancels: halving doubles on tensor-core hardware, so on essentially every modern accelerator, in BF16, FP8, or FP4. Count tokens in flight, not requests, chunked-prefill tokens count too.
Picking for an SLO
Invert TPOT in the memory-bound regime: Example: ms, TB/s, GB, , KiB: Then take , SLO, memory, and diminishing returns.
Closing the loop with load
Little’s Law ties offered load to the batch you will actually see: If the queue grows without bound and P99 diverges, that is an admission-control problem, not a batching one. See Scheduling and Admission Control and Latency Decomposition.
| Changes | Effect |
|---|---|
| GQA/MLA | shrinks → raises the throughput ceiling |
| KV quantization | same, ~2× |
| Weight quantization | shrinks → flat region extends further |
| Speculative Decoding | trades spare FLOPs for latency; only helps below |
| Disaggregated Inference | lets prefill and decode sit at different batch sizes |