THIS EXPLANATION
THE ROOM
CDA·16 Computing, Data & AI 6 MIN · 8 STATIONS

Batching and the throughput trade

A Socratic walk-through of batching and the throughput trade — reasoned out one step at a time, not lectured.

abcdefgh
a

The question we started with

THE QUESTION #

Why does serving more users at once make each individual one wait longer?

There is a claim that sounds like it must be a mistake: the way to make a model server efficient is to make each user wait. Not as a side effect to be minimised — as the mechanism. Group requests together, and the machine does far more total work per second; leave them alone, and it idles.

That should bother you, because we usually think of contention as pure loss. If two people share a road, both go slower and nothing is gained. So what is different about a GPU that makes sharing produce something rather than merely dividing it?

b

Reasoning it through

REASONING #

The answer is in a detail of how the hardware spends its time, so let us look there.

When a model generates one token for one request, the arithmetic involved is small — a handful of multiplications against each weight. But to do that arithmetic, every weight in the model must be read out of memory and brought to the arithmetic units. For a large model that is tens of gigabytes moved to produce a single token. So which resource is the bottleneck: the multipliers, or the pipe from memory?

The pipe, overwhelmingly. And here is the consequence that makes the whole thing work: once the weights have been fetched, they can be used for many requests at almost no extra cost. Fetching the weights to serve one sequence and fetching them to serve thirty-two costs nearly the same in memory traffic. The arithmetic multiplies; the expensive part does not.

So the machine has been sitting there with its arithmetic units idle, waiting on memory, and batching fills that idle capacity with other people's work. That is why it is not like the shared road. The road was saturated; the GPU was not.

Now, what does the individual pay? Two separate costs, and it is worth keeping them apart.

The first is waiting to be grouped. If the server collects requests for fifty milliseconds before starting, everyone pays up to fifty milliseconds. That cost is pure delay, added before any work happens.

The second is sharing the step. Once the batch is large enough that the arithmetic units are actually busy, adding another request no longer rides along free — it competes. Each step now takes measurably longer, so every member of the batch produces tokens more slowly. Throughput is still climbing, but the curve is bending over, and each request's own experience is degrading.

There is a third limit that decides where you can stop. Every sequence in flight needs its attention keys and values kept in memory — the KV cache — and that memory grows with batch size and with each sequence's length. Long before the arithmetic units are the constraint, you can simply run out of room to hold everyone's context.

So what shape does the trade have? Throughput rising steeply, then flattening; per-request latency flat, then rising. Where would you choose to sit?

c

The analogy

THE ANALOGY #
THE FIGURE

Think of a lift in a tall building. Sending it up for one person is enormously wasteful — almost all the energy goes into moving the lift itself, not the passenger. Wait a moment for others and the same journey carries ten people for nearly the same cost, which is why the building's capacity depends far more on how full each trip is than on how fast the lift travels. But that filling has a price paid entirely by the individual: you stand in the lobby while it fills, and then you stop at seven floors instead of one.

WHERE IT BREAKS DOWN

a lift is genuinely full at some point and physically cannot take an eleventh passenger, whereas a GPU's batch has no hard capacity — the degradation is gradual, and the real wall is memory for the KV cache rather than any limit on how many sequences may be in flight.

d

Clarifying the model

THE MODEL #

The naive version of this trade is harsher than it needs to be, and the fix changed how these systems are built.

If you form a fixed batch, run it to completion, and only then start the next, you have static batching — and it has a nasty property. The batch finishes when its longest member finishes. A request wanting twenty tokens sits in the machine while a neighbour generates two thousand, contributing nothing and occupying a slot. The short request's latency is set by the longest sequence it was unlucky enough to be grouped with.

Continuous batching — introduced by the Orca system in 2022 and made widely available by vLLM in 2023 — schedules at the level of individual decode steps instead. When a sequence finishes, its slot is immediately given to a waiting request; new arrivals join the very next step rather than the next batch. This removes the head-of-line blocking almost entirely, which is why the naive picture of "large batches mean long waits" is much less true of a modern server than of a 2021 one.

Two misconceptions to correct. First, batching does not make any single request faster — it never has and never can. It makes the fleet cheaper, and cheaper capacity is what lets you keep queues short at a given load. The user-visible benefit is indirect. Second, the trade is not between latency and throughput in the abstract; it is between latency and cost, because throughput divided by hardware is what appears on the invoice.

And Little's law sits quietly behind all of it: push the arrival rate up without adding capacity and the number of requests resident grows whatever your batching policy is. Batching does not repeal queueing theory; it moves the point at which the queue starts to grow.

e

A picture of it

THE PICTURE #
Batching and the throughput trade
Batching and the throughput trade the bars are total tokens produced per second by the server; the line is how long one request waits for its own answer. Both are shown as a percentage of their largest value, and the numbers are an illustrative shape rather than a measurement of any particular deployment. Read left to right: through the first few steps the bars climb steeply while the line barely moves -- that is idle memory bandwidth being filled, and it is nearly free. Past the middle the bars flatten while the line turns sharply upward: extra concurrency now buys little throughput and costs each user a great deal. The operating point you want is the knee, not either end. {"generator":"mermaid-svg-renderer@3.2.1","source":"../Socrates/.diagram-cache/_src/batching-and-the-throughput-trade.md","sourceIndex":1,"sourceLine":4,"sourceHash":"cc260a19b30900d2999257892e224b48c42f90bcbbdb5e0cf84cd127dfcf1ed1","diagramType":"xychart","layoutVariant":"source","repairedDuplicateIds":[],"motion":"entrance-with-reduced-motion-fallback","presentation":"editorial","attempt":1,"viewBox":{"x":0,"y":0,"width":790,"height":668},"qa":{"passed":true,"findings":[]}} 1 4 8 16 32 64 Sequences decoded together 100 90 80 70 60 50 40 30 20 10 0 Relative to the best observed

How to readthe bars are total tokens produced per second by the server; the line is how long one request waits for its own answer. Both are shown as a percentage of their largest value, and the numbers are an illustrative shape rather than a measurement of any particular deployment. Read left to right: through the first few steps the bars climb steeply while the line barely moves — that is idle memory bandwidth being filled, and it is nearly free. Past the middle the bars flatten while the line turns sharply upward: extra concurrency now buys little throughput and costs each user a great deal. The operating point you want is the knee, not either end.

f

What became clearer

WHAT CLEARED #
WHAT CLEARED

Batching works because the dominant cost of generating a token — hauling the model's weights out of memory — is paid once per step regardless of how many sequences share that step. So concurrency converts idle bandwidth into finished work, and the individual pays first in grouping delay and later in genuine contention. The result is a curve with a knee rather than a straight trade, and the engineering question is where on that curve your users' patience and your hardware budget intersect.

g

Where to go next

ONWARD #
  • Prefill and decode have different bottlenecks, which is why some servers schedule them separately or on different machines.
  • How KV-cache memory management — paged allocation in particular — sets the real ceiling on batch size.
  • Little's law and queueing behaviour near saturation, where waiting time stops rising gently and starts rising sharply.
h

Key terms

TERMS #
TermWhat it means
Memory bandwidth bounda computation whose speed is limited by moving data to the processor rather than by the arithmetic itself; the normal condition during token generation.
KV cachethe stored attention keys and values for a sequence, which let each new token attend to earlier ones without recomputation, and which consume memory in proportion to batch size and context length.
Static batchingforming a fixed group and running it to completion, so every member waits for the longest.
Continuous batchingadmitting and retiring sequences between individual decode steps, removing most of the waiting that static batching imposes.
Little's lawin a stable queueing system, the average number of items inside equals the arrival rate multiplied by the average time each spends there.

Every term the collection defines is gathered in the glossary.

Nearby on the shelf

4