← Back to the post: prefill and decode

Prefill and Decode Lab

AI Infrastructure series · part 13 · the two stages of every answer
Preset workload
Model
GPU

What you are looking at: one request on an otherwise idle GPU. Prefill reads the whole prompt in one pass: heavy maths, and it sets how long you wait for the first word (TTFT). Decode writes the answer one token at a time: light maths but a full read of the weights and the KV cache for each token, so memory speed sets the time per token (TPOT).

Try this: pick Chatbot and note the prefill share, then pick RAG and Coding agent. Then push the prompt to 128,000 tokens and watch TTFT jump far more than 64 times the 2,000-token figure (attention grows with the square of the prompt) while TPOT doubles (bigger KV cache to read).

Request

Prompt length
Answer length

One request, start to finish (to scale)

prefill: reading the promptdecode: writing the answer

What you are looking at: 30 users are already streaming answers, each with 2,000 tokens of history. Each mark is one token written for one of them. Then a new user's long prompt arrives. Without chunking its whole prefill runs as one step and everyone freezes; with chunked prefill one chunk rides along in each step; with separate prefill GPUs the decode GPUs never see it.

Try this: set the incoming prompt to 32,000 tokens and compare the freeze with the chunked lane. Then change the chunk size from 256 to 8,192: small chunks keep each step short but take more steps; big chunks get the newcomer to its first token sooner but slow everyone else more per step.

Incoming request

Incoming prompt
Chunk size (token budget for prefill)
Without chunked prefilleveryone waits for it
With chunked prefillone chunk per step
Separate prefill GPUsdisaggregated

What you are looking at: a roofline for the chosen GPU. The x axis is arithmetic intensity: FLOPs done per byte read. Left of the ridge, memory bandwidth sets the speed; right of it, compute does. Prefill (red) does about as many FLOPs per byte as it has tokens. Decode (black) does about one per sequence in the batch, minus what the KV cache reads cost.

Try this: move the batch from 1 to 256 and watch the decode point climb the slope, then stall. Now cut the history to 200 tokens: with less KV cache to read, the same batch climbs much higher. Finally push the batch past the memory limit and see the warning.

Decode batch

Sequences in the batch
History per sequence

Prefill

Tokens in one prefill pass

Total decode tokens per second by batch size

What you are looking at: requests that start with the same text (system prompt, tool definitions, a shared document, or an agent's earlier conversation). With prefix caching, the KV blocks for that shared beginning are kept and reused, so only the new tail is prefilled. The hit rate is the share of requests that land on a GPU holding the prefix.

Try this: pick Coding agent: almost the whole prompt is shared, so a hit removes nearly all of the prefill. Then drop the hit rate to 10% (what round-robin across ten replicas gives you) and watch the saving vanish. Set the shared part to 0 to see a workload caching cannot help.

Traffic

Shared prefix
New tokens per request
Requests per second
Cache hit rate
Requests running at once

Prefill per request

KV memory held for the shared prefix

What you are looking at: speculative decoding for one user. A small draft model guesses the next few tokens; the big model checks them all in one pass, which costs about the same as one normal decode step because decode is limited by reading the weights, not by maths. Accepted guesses are free tokens; the first wrong guess is replaced by the big model's own token.

Try this: start at 70% acceptance and find the number of drafted tokens with the best speed-up. Then drop acceptance to 40% (creative writing) and see more drafting start to hurt. Switch the draft model to a larger one: better guesses cost more time per guess.

Speculation

Draft model
Tokens drafted per round
Chance each guess is accepted

Speed-up by tokens drafted (at this acceptance)

Estimates, illustrative: prefill at about 60% of peak Tensor Core speed (2 operations per weight per token, plus attention between prompt tokens), decode at about 80% of peak memory bandwidth (all weights plus the KV cache read per step). 16-bit weights. Llama 3.1 shapes: 8B has 32 layers, hidden size 4,096, 128 KB of KV per token; 70B has 80 layers, hidden size 8,192, 320 KB per token and runs on 2 GPUs with tensor parallel. GPUs: H100 989 TFLOPS BF16 dense, 3.35 TB/s, 80 GB; H200 989 TFLOPS, 4.8 TB/s, 141 GB; B200 2,250 TFLOPS, 8 TB/s, 192 GB. The roofline uses ideal peaks. KV pool = 90% of memory minus weights. Real engines and workloads vary.