LLM Serving at Scale: Prefill, Decode & Continuous Batching

How LLM inference is served: prefill and decode, time to first token and time per output token, batching and scheduling, and why KV-cache memory often limits batch size.

Advanced · Undergraduate mathematics · about 22 minutes

Reading map and next steps

Intuition

The idea in plain words, before the symbols.

Serving an LLM is not "run the model once". It means running the model for many users at once while keeping each user's wait short and the GPUs busy.

Generating a response has two phases with different costs:

  • Prefill processes the whole prompt in one parallel pass. It is made of large matrix multiplications and is usually limited by arithmetic. Its result is the first output token and the KV cache: the keys and values of every prompt token at every layer, kept so they are not recomputed.
  • Decode then generates one token per step. Each step must read every weight, and the request's whole KV cache, from GPU memory, but does little arithmetic per byte read. At small batch sizes decode is therefore limited by memory bandwidth, whatever the context length.

Batching helps decode because the weights are read once per step and shared by every sequence in the batch. The KV cache cannot be shared that way: each sequence has its own. At small batches and short contexts, reading the weights dominates each step; at large batches and long contexts, reading the KV caches does (Pope et al., 2023, §2). Long contexts therefore slow every decode step, and because each sequence's cache must stay in memory, they also limit how many sequences fit in a batch. The KV cache becomes the main resource a serving system schedules around (Kwon et al., 2023).

Continuous batching keeps the GPU busy. A static batch waits until its longest request finishes; with iteration-level scheduling the server re-forms the batch at every decode step, admitting new requests and retiring finished ones (Yu et al., 2022).

Throughput alone is the wrong target if requests wait too long. Serving systems set latency targets, called service-level objectives (SLOs), for the time to the first token and the time per later token, and measure goodput: the rate of requests that meet those targets (Zhong et al., 2024).

Study prompts for this section: Intuition
Study prompts

Each button copies a prompt about this section to your clipboard, for an AI assistant you already use. Nothing is sent by this site.

Mathematics

Definitions, assumptions and the derivation.

Latency decomposition (a serving mental model)

Let ToutT_{out} be the number of generated tokens. A simple decomposition is:

Latency≈TTFT⏟time to first token+(Tout−1)⋅TPOT⏟time per output token.\text{Latency} \approx \underbrace{\text{TTFT}}_{\text{time to first token}} + (T_{out}-1)\cdot\underbrace{\text{TPOT}}_{\text{time per output token}}.
  • TTFT mostly reflects prefill, plus any time the request waits in a queue.
  • TPOT reflects decode: the weight and KV-cache reads of each step.

KV cache memory scaling (why long prompts hurt)

Across a batch of BB sequences and LL layers, with HkvH_{kv} key-value heads of dimension dheadd_{head}, storing keys and values for TT tokens costs roughly:

MemKV≈B⋅L⋅T⋅Hkv⋅dhead⋅2⋅bytes.\mathrm{Mem}_{KV} \approx B\cdot L\cdot T\cdot H_{kv}\cdot d_{head}\cdot 2 \cdot \mathrm{bytes}.

The factor of 2 is for storing both KK and VV, and "bytes" is the size of one element (2 for fp16). Every decode step reads this whole cache, so for long contexts and large batches it sets both the memory needed and much of the time per step.

Goodput (a serving SLO mental model)

If the latency targets are STTFTS_{TTFT} and STPOTS_{TPOT}, one simple definition counts only the requests that meet both:

Goodput=Throughput×Pr⁡ ⁣(TTFT≤STTFT∧TPOT≤STPOT).\text{Goodput} = \text{Throughput} \times \Pr\!\left(\text{TTFT} \le S_{TTFT} \wedge \text{TPOT} \le S_{TPOT}\right).

A system that is fast on average can still have low goodput if many requests miss a target. DistServe (Zhong et al., 2024) reports goodput as the highest request rate at which a chosen share of requests, such as 90%, meets both targets.

Paging / fragmentation waste

If KV memory is allocated in blocks of bb tokens, then for a sequence length TT:

allocated(T)=⌈Tb⌉b,waste(T)=allocated(T)−T.\text{allocated}(T) = \left\lceil \frac{T}{b} \right\rceil b,\qquad \text{waste}(T) = \text{allocated}(T) - T.

With blocks, at most b−1b-1 token slots per sequence are wasted, in its last block. Reserving one contiguous region per request for the longest possible output wastes far more. PagedAttention allocates blocks on demand, like pages of virtual memory, which lets more sequences share the GPU under continuous batching (Kwon et al., 2023).

Refilling a freed slot, counted

Continuous batching is easiest to see by counting rounds. Take C=2C=2 sequence slots and four requests that still owe 11, 44, 11 and 44 output tokens, queued as A, B, C, D. One decode round gives every active request one token. If a schedule takes RR rounds, it offers CRCR slot-rounds; the requests use W=∑iri=10W=\sum_i r_i=10 of them, where rir_i is what request ii owes, and

idle=CR−W,utilization=WCR.\text{idle}=CR-W,\qquad \text{utilization}=\frac{W}{CR}.

With fixed batches, A and B hold both slots until B finishes, then C and D do the same. The rounds have 2,1,1,1,2,1,1,12,1,1,1,2,1,1,1 active requests: R=8R=8, 66 slot-rounds are idle, and the utilization is 10/16=5/810/16=5/8. With refill, a slot freed at the end of a round goes to the next request in the queue. The rounds hold (A, B), (B, C), (B, D), (B, D), (D), (D): R=6R=6, 22 slot-rounds are idle, and the utilization is 10/12=5/610/12=5/6. C no longer waits for B.

Refill does not always save rounds. Queue the same requests as B, D, A, C: both rules run B and D together for four rounds and A and C for one, with no idle slot. Requests of equal length that start together leave nothing to refill. Over all 24 orders of these four requests, fixed batches take 5 or 8 rounds and refill takes 5 or 6.

The counts assume every decode round costs the same, and they leave out prefill, KV-cache reads and the scheduler's own work, so they are not latencies or measured throughput. The refill rule is Orca's iteration-level scheduling (Yu et al., 2022); the requests and the numbers are this page's example.

Study prompts for this section: Mathematics
Study prompts

Each button copies a prompt about this section to your clipboard, for an AI assistant you already use. Nothing is sent by this site.

Code

The same calculation as runnable code, in the notation of the derivation.

import numpy as np

def kv_gb(T, layers, h_kv, d_head, batch=1, bytes_per_elem=2):
    elems = batch * layers * T * h_kv * d_head * 2  # K and V
    return elems * bytes_per_elem / 1e9

def waste_tokens(T, b):
    return int(np.ceil(T / b) * b - T)

L, Hkv, dh, B = 80, 8, 128, 16  # example: 80 layers, grouped-query attention with 8 KV heads, fp16
for T in [2048, 8192, 32768, 131072]:
    print("T=", T, "KV~", round(kv_gb(T, L, Hkv, dh, batch=B), 2), "GB")

b = 256  # tokens per block
for T in [2000, 8192, 20000]:
    print("T=", T, "waste_tokens=", waste_tokens(T, b))

Count decode rounds under both rules

This standard-library Python code counts the rounds, idle slot-rounds and utilization of both rules for both queue orders above. It counts rounds; it does not time anything.

SLOTS = 2
QUEUE = (("A", 1), ("B", 4), ("C", 1), ("D", 4))
REORDERED = (("B", 4), ("D", 4), ("A", 1), ("C", 1))

def schedule(queue, rule, slots=SLOTS):
    """Each round gives every active request one token; returns rounds, idle, utilization."""
    position = {name: i for i, (name, _) in enumerate(queue)}
    rounds = []

    def run(active):
        active = sorted(active, key=lambda request: position[request[0]])
        rounds.append(tuple(name for name, _ in active))
        return [(name, left - 1) for name, left in active if left > 1]

    if rule == "fixed":
        for start in range(0, len(queue), slots):
            batch = list(queue[start:start + slots])
            while batch:              # the batch keeps its slots until all finish
                batch = run(batch)
    elif rule == "refill":
        waiting = list(queue)
        active = [waiting.pop(0) for _ in range(min(slots, len(waiting)))]
        while active:
            active = run(active)
            while len(active) < slots and waiting:   # refill freed slots
                active.append(waiting.pop(0))
    else:
        raise ValueError(rule)

    busy = sum(steps for _, steps in queue)
    offered = slots * len(rounds)
    return rounds, offered - busy, busy / offered

fixed = schedule(QUEUE, "fixed")
refill = schedule(QUEUE, "refill")
assert [len(r) for r in fixed[0]] == [2, 1, 1, 1, 2, 1, 1, 1]
assert fixed[1:] == (6, 10 / 16)
assert refill[0] == [("A", "B"), ("B", "C"), ("B", "D"), ("B", "D"), ("D",), ("D",)]
assert refill[1:] == (2, 10 / 12)

for label, queue in (("A, B, C, D", QUEUE), ("B, D, A, C", REORDERED)):
    for rule in ("fixed", "refill"):
        rounds, idle, utilization = schedule(queue, rule)
        print(f"{label}  {rule:6}  rounds={len(rounds)}  idle={idle}  utilization={utilization:.3f}")
Study prompts for this section: Code
Study prompts

Each button copies a prompt about this section to your clipboard, for an AI assistant you already use. Nothing is sent by this site.

Demo

Change an input, predict the result, and see which quantities respond.

ExploreChange one control at a time and name what responds.An optional observation guide follows the demo.

Explore LLM Serving at Scale: Prefill, Decode & Continuous Batching

Loading the interactive demo…

Observation guide

Optional and ungraded. Opening a guide saves nothing.

Choose what to inspect in LLM Serving at Scale: Prefill, Decode & Continuous Batching, then open its guide.

The demo is a toy latency calculator with illustrative numbers, not a benchmark. For prompt length PP (in tokens), output length ToutT_{out} and batch size BB it computes TTFT=0.05 PB\text{TTFT}=0.05\,P\sqrt{B} ms and TPOT=5+0.5B\text{TPOT}=5+0.5B ms per output token, then the decomposition above, TTFT+(Tout−1)⋅TPOT\text{TTFT}+(T_{out}-1)\cdot\text{TPOT}, with the first output token counted in TTFT. The 0.5B0.5B term stands in for the per-step KV-cache reads, and the batch size is one fixed number, not a scheduler.

Each phase challenge states its fixed inputs and its rule before you answer, and the calculator stays open while you do. A phase dominates when it is more than 55% of the modelled total; about equal means each phase is 45–55%. The doubled-batch challenge compares growth in milliseconds, not percentage growth. The coloured summary line uses separate 30%/70% bands and is not a grade. The workload cards show rough indicators that the latency numbers do not use.

The calculator models one request. It has no stream of requests, queue or throughput, so it cannot compute goodput; you can compare its TTFT and TPOT with a target you choose. The demo does not use the KV-memory and paging formulas; the code above computes them. The practice after the demo counts a small queue of requests under fixed batches and refill.

Study prompts for this section: Demo
Study prompts

Each button copies a prompt about this section to your clipboard, for an AI assistant you already use. Nothing is sent by this site.

Practice

Count decode rounds for four queued requests under fixed batches and under refill, then reorder the queue and count again.

Loading the exercise…
In focus

Concept: LLM Serving at Scale: Prefill, Decode & Continuous Batching

What is the smallest example of LLM Serving at Scale you can work through by hand, and what does it show?

BeforeScaled Dot-Product Attention & Transformer LayersNextQuantization: Compressing Models to Integers
DetailsLLM Systems

After the first pass

Go deeper

Optional material for a second reading. Sources are always shown; the other panels open from their headings or from these links, and closing one keeps what you wrote or revealed.
What to look for in each sectionChoose a section and what you expect to see in it, then read short advice.

Section by section

Choose what to look for in each section

How LLM inference is served: prefill and decode, time to first token and time per output token, batching and scheduling, and why KV-cache memory often limits batch size.

Choose what to look for (no demo yet)01 / Intuition
Illustration of prefill, KV cache shelves, continuous batching lanes, and decode token streams.
What to look for

Start with the picture, metaphor, or geometric mechanism.

Choose first

Choose what you expect to see in this section of LLM Serving at Scale: Prefill, Decode & Continuous Batching.

Questions to ask of the pictureChoose a part of the picture and what you expect, then read short advice.

Looking at the picture

Questions to ask of the picture

How LLM inference is served: prefill and decode, time to first token and time per output token, batching and scheduling, and why KV-cache memory often limits batch size.

4 of 4 sections writtenNo live demo yet
Question

Which part of the picture should you look at first?

Choose first

Pick the part of the picture you expect to explain LLM Serving at Scale: Prefill, Decode & Continuous Batching.

Sources

References for this page

A reference shows where an idea comes from; it does not vouch for every step on this page.
paper · 2022Orca: A Distributed Serving System for Transformer-Based Generative ModelsYu et al.
Why it is cited

Treats generative transformer serving as many iterations, each producing one output token per request, and introduces iteration-level scheduling, which re-forms the batch at every iteration, together with selective batching (OSDI 2022).

Open source
paper · 2023Efficient Memory Management for Large Language Model Serving with PagedAttentionKwon et al.
Why it is cited

Shows that the large, dynamically growing KV cache wastes memory through fragmentation and over-reservation, limiting batch size; PagedAttention stores it in fixed-size blocks like virtual-memory pages, and vLLM builds on it (SOSP 2023).

Open source
paper · 2023Efficiently Scaling Transformer InferencePope et al.
Why it is cited

Models inference cost: each step moves the weights and KV cache from memory; loading weights dominates at small batches and short sequences, loading the KV cache at large batches and long sequences (§2) (MLSys 2023).

Open source
paper · 2024DistServe: Disaggregating Prefill and Decoding for Goodput-optimized Large Language Model ServingZhong et al.
Why it is cited

Defines time to first token (TTFT) and time per output token (TPOT), measures goodput as the highest request rate at which a target share of requests meets both targets, and splits prefill and decode across GPUs (OSDI 2024).

Open source
What each claim rests on1 claim, each with its sources, where it appears on this page, and its caveats.

Claims and sources

How LLM inference is served: prefill and decode, time to first token and time per output token, batching and scheduling, and why KV-cache memory often limits batch size.

Checked against the cited passages

The checks were made by this site, not by an independent reviewer.

LLM serving is a scheduling and memory-management problem over many iterations: decoding advances one token per request per iteration, the batch can change between iterations, and KV-cache memory can limit throughput.
What the sources support

Orca supports the scheduling part: generative Transformer serving is multi-iteration autoregressive inference with one output token per iteration, and fixed request-level batches motivate iteration-level sch...

Where to see it on this page
Equation 1
Latency≈TTFT⏟time to first token+(Tout−1)⋅TPOT⏟time per output token.\text{Latency} \approx \underbrace{\text{TTFT}}_{\text{time to first token}} + (T_{out}-1)\cdot\underbrace{\text{TPOT}}_{\text{time per output token}}.
Equation 2
MemKV≈B⋅L⋅T⋅Hkv⋅dhead⋅2⋅bytes.\mathrm{Mem}_{KV} \approx B\cdot L\cdot T\cdot H_{kv}\cdot d_{head}\cdot 2 \cdot \mathrm{bytes}.
Caveat

This covers the serving mechanism. TTFT and TPOT follow DistServe; the goodput formula is a simpler version of its definition, and the KV-memory formula is a standard count. The demo's coefficients are illus...

StatusChecked against the cited passages.

Orca supports autoregressive transformer serving as many iterations that each emit one token, which motivates iteration-level scheduling and selective batching because fixed request-level batches waste capacity. PagedAttention supports the KV cache as large, dynamic, fragmentation-prone memory that can limit batch size and throughput.

Explain it without the pageWrite your own explanation before asking for feedback. Your draft and any hints stay while this page is open.

Practice · LLM Serving at Scale: Prefill, Decode & Continuous Batching

Try the idea in your own words

How LLM inference is served: prefill and decode, time to first token and time per output token, batching and scheduling, and why KV-cache memory often limits batch size.

Concept · Selected for practice

LLM Serving at Scale: Prefill, Decode & Continuous Batching

What it rests on: Sources: Orca: A Distributed Serving System for Transformer-Based Generative Models; Efficient Memory Management for Large Language Model Serving w...

Context and links
Choose a task

Explain the mechanism

For LLM Serving at Scale: Prefill, Decode & Continuous Batching: What is the smallest example of LLM Serving at Scale you can work through by hand, and what does it show? Explain your answer, including what changes, why, and which assumption matters.

No answer yet

A rough first thought is enough. Your draft stays when you change tasks.

Your draft stays on this page and is cleared when you leave or reload.

A little help · Explain

Open one hint at a time. These are suggestions, not your answer or a grade.

0 of 3 hints shown for this question.

    Clearing your answer keeps this help record. Outside help is your own report; this page cannot check it.

    Where am I stuck? (optional)
    Your own description, not an automatic diagnosis

    Choose one, or leave this unspecified. Select it again to clear it.

    Take your draft to a feedback conversation

    No AI feedback runs here. You can copy a prompt to use elsewhere; nothing is sent automatically. Review the text before sharing, and leave out private information.

    Write an attempt before copying a feedback prompt.

    A draft, or an AI reply to it, is not a test of what you have learned. To check that, try a different case later without help.

    Ask about this pagePick one item on this page and copy a prompt about it into an AI assistant you already use. Notes you write here stay in this browser.
    Ask about this pageClose
    ConceptLLM Serving at Scale: Prefill, Decode & Continuous BatchingSources: Orca: A Distributed Serving System for Transformer-Based Generative Models; Efficient Memory Management for Large Language Model Serving w...assert [len(r) for r in fixed[0]] == [2, 1, 1, 1, 2, 1, 1, 1]

    Your question

    Choose what your question is about

    Pick the idea, equation, source, code, claim, misconception or demo state you want to ask about. The prompt you copy includes it, so the answer can stay on that item.
    Next local actionNo local draft saved yet

    Open the draft below to save one note and next action in this browser.

    conceptLLM Systems

    LLM Serving at Scale: Prefill, Decode & Continuous Batching

    Starting question

    What is the smallest example of LLM Serving at Scale you can work through by hand, and what does it show?

    SourcesCheck the 4 cited sources listed under Sources.Notes can be saved for this item
    Points of view on this item

    Fixed prompts generated from this item. They are not comments from people or independent reviews.

    Learner’s first stepAsk what would make "LLM Serving at Scale: Prefill, Decode & Continuous Batching" feel predictable rather than familiar.
    Assumption

    The 4 cited sources must support this exact item, not just the surrounding topic.

    Source-checking summary

    Connect the definition to one equation, piece of code or demo before widening the discussion.

    Proposed experiment

    Change one input, then check that the same relationship holds in the mathematics, the code and the demo.

    Next action

    You can state the mechanism in your own words

    Evidence4 checks
    ObservationChecking for a saved observation
    ActionReady for one action
    PromptLearner prompt ready to copy
    Go to this item
    01ObservationChecking what this browser has saved
    02EvidenceChecking for a saved observation
    03SourcesCheck the 4 cited sources listed under Sources.
    04Next stepSave one next action
    Local action draftNo local draft saved yetOpen when you are ready to write one next action
    Local action draft

    This draft stays in this browser and is attached to this item only.

    No local draft saved.
    Evidence to inspect
    • What the 4 cited sources say about this exact item
    • The definition, its prerequisites, and a contrasting concept
    • The equation or code that makes the concept concrete
    • One demo state that shows what stays the same as the inputs change
    What would resolve this
    • You can state the mechanism in your own words
    • You can name the prerequisite that would clear up the confusion
    • You can predict how the result changes when one input changes
    Reference idconcept/concept-notebook/llm-systems/llm-serving concept:llm-systems/llm-serving