The KV Cache is the second-largest consumer of HBM after weights, and it is what actually caps concurrency.

The formula

SymbolMeaning
layers
KV heads
head dim
bytes per element (2 for FP16/BF16, 1 for FP8)
, sequence length, batch

Per-token, per-sequence cost, memorize this one:

Reducing

With query heads and group size , GQA divides the cache by . See Attention Variants for Inference.

  • MHA:
  • GQA: , typically
  • MQA:

MLA (latent compression)

Cache a single low-rank latent plus a small decoupled RoPE part , shared across all heads: Example , , , : KB/token, versus MB/token for equivalent MHA. ~57× compression. The cost is that K and V must be re-expanded (or the projections absorbed into /) inside the kernel.

Worked numbers

8B model: , , , .

Scheme32K context, 1 seq
MHA ()512 KiB16 GB
GQA-8 ()128 KiB4 GB
GQA-8 + FP8 KV64 KiB2 GB
MQA ()16 KiB0.5 GB

How it caps batch size

80 GB device, GB, 6 GB workspace/activations/graphs → 66 GB free. With KiB that is 503K cached tokens: 245 sequences at 2K context, or 61 at 8K, or 15 at 32K. Concurrency falls linearly in context length.

Fragmentation eats into this unless you use Paged Attention; reuse recovers some via Prefix Caching.

Rule of thumb

. Divide free HBM by it to get your total token budget, that number, not “batch size”, is what the scheduler is really allocating.