Everything inference: an intro
Follow one request, "The capital of France is", from the user to the GPU and back. Each stop explains one part of serving an LLM, lets you try it, and links to the posts that go deeper.
This is an introduction: each stop explains one idea with one example, and many techniques production systems use are left out.
Scroll to follow the request ↓
The request
What the request looks like
Before any GPU work, a request has a shape: how many tokens go in, how many come out, and how fast the user needs them. Chat, reasoning, summarization and agents have very different shapes, and each layer below matters more for some of them than for others.
Picking a replica
A gateway takes the request and a router picks which copy of the model (a replica) serves it. Before answering, a replica runs prefill, one pass over the whole prompt that saves each token's keys and values: the KV cache. A router that knows which replica already holds this prompt's KV cache can skip most of that work.
One replica
The engine
Each replica runs an engine such as vLLM or SGLang. Its API server takes the request, the scheduler batches it with other requests, the KV cache manager finds room for its cache, and the model runner does the GPU work.
Kernels
The GPU runs small programs called kernels, one after another: matrix multiplies, adds, and the handful of kernels that make up each norm. The CPU launches each one, and when the GPU finishes a kernel before the next launch arrives, it waits. This measurement used Hugging Face transformers, which launches every kernel from Python; an engine like vLLM runs the same model with fewer, larger kernels and replays them with CUDA graphs.
Many users at once
After prefill, the model makes the answer one decode step at a time, one new token per request per step. In a dense model like Qwen3-8B, each step reads every weight once, however many requests share it, so the engine puts many users' next tokens in the same step. The first users added cost almost nothing, until the compute catches up and every step gets slower.
Inside the GPU
Prefill, decode and the KV cache
Prefill runs the whole prompt through the model and stores each token's keys and values: the KV cache. Engines can split a long prompt into chunks, as stop 10 shows. Decode then produces one token per call, reusing the cache and adding one token's worth to it each time.
Why decode gets slower
Every decode step reads the model's weights and the whole KV cache out of memory. As the conversation grows, so does the cache, and each new token takes longer.
Guessing ahead
With one user, a decode step leaves the GPU's compute mostly idle while it waits for the weights. Speculative decoding fills that time: something cheap guesses the next few tokens, and the model checks them all in one step, keeping every guess up to the first one it disagrees with.
Weight precision
Every weight is stored with a fixed number of bits, from 16 in BF16 down to 4 in FP4. Fewer bits means fewer GPUs to hold the model, more room for the KV cache and, on GPUs that compute in that format, faster tokens, at some cost in accuracy.
Prefill stalls decode
When a long prompt is prefilled on the GPU where other users are decoding, their tokens stop until that prefill finishes. Chunking the prefill trades one long stall for many short ones, and running prefill on a separate GPU removes the stall, as long as handing over the cache doesn't block decode.
Between GPUs
Moving the cache
Once prefill and decode run on different GPUs, the KV cache has to travel between them. Inside one machine over NVLink that takes a few milliseconds. Across a network it is predicted to take 7 to 27 times longer; that hasn't been measured yet.
Does it fit in one NVLink domain?
GPUs inside one NVLink domain copy data between each other at hundreds of GB/s, and beyond it traffic goes through network cards at about 50 GB/s. On an HGX B200 that domain is one 8-GPU node, and on a GB200 NVL72 it is a whole 72-GPU rack.
Back to the user
Each token streams back through the router and gateway (in some stacks one program) to the chat as soon as it is made. Benchmarks split the wait in two: TTFT, the time to the first token, which is mostly queueing plus prefill, and TPOT, the average gap between the tokens after it, which is mostly decode plus any stalls from other requests' prefills.
The next turn
A chat app sends the whole conversation again on every turn, and an agent resends its context on every call. If the server still has the KV cache for the part it has seen, it only prefills what's new. That's prefix caching. Only a matching start counts: the tokens must be the same from the first one, and the cache lasts until it is evicted to make room.
Running it
Adding replicas
When traffic grows, the autoscaler adds replicas. Without its compile caches, a new replica takes minutes before it serves anything, most of it spent compiling kernels. Keeping the caches cut a 7B's start to about a minute and a 32B's to about two. Either way scaling has to start before the queue is already long.
When hardware fails
At scale, hardware fails often. How many GPUs one failure takes down depends on the NVLink domain and the replica size. Requests running on those GPUs lose their KV cache, so another replica has to prefill them again before it can carry on.
Rolling out a new model
A rolling update restarts every replica on purpose. Draining requests in flight, canaries, and keeping prefill and decode on the same model version keep users from noticing.
Planning
How many GPUs?
Sizing uses every layer above. Traffic and latency targets give the requests in flight, memory and decode speed give requests per replica, prefill takes its share, and then you add spares.