TechByteByByte

Inference

Production-oriented: what actually happens when you send a prompt to an LLM — prefill, decode, and the KV cache — with a verified FLOPs analysis showing exactly why decode without caching is catastrophically expensive, and why caching fixes it.

#LLM#AI#Inference#Prefill#KV Cache#Latency

Before you continue: three tools for this module

  • Inference: using fixed learned parameters to produce output.
  • Logit: a raw score for a possible next token.
  • Decoding: the rule that chooses tokens from model scores.

You do not need to memorize these yet. Use this map when the terms reappear.

Begin with the central question

What hidden problem does Inference solve inside a real language-model system?

Keep that central question about Inference in mind. The definitions, numbers, diagrams, and code examples below answer it one piece at a time.

trained parameters + prompt → forward passes → generated response

1. What You Will Learn

Learning outcomes

  • Define inference and distinguish it from training.
  • Separate prompt prefill from token-by-token decoding.
  • Trace latency, throughput, memory, and KV-cache effects.
  • Explain what an inference server contributes beyond the model itself.

In one sentence

💡 Big picture

Inference is the stage where a trained model uses fixed learned parameters to answer a new prompt.


2. Why This Module Exists

The problem this module solves

  • Training teaches the model; inference uses what was learned.
  • Understanding prefill, decoding, caching, and serving explains why responses have different speeds and costs.

3. Intuition

processing your prompt for the first time (prefill) is a very different computational problem from generating each subsequent token (decode). Prefill processes many tokens in parallel, once. Decode processes one new token at a time, repeatedly — and without caching, it would redundantly reprocess the entire growing sequence at every single step.


4. Core Concept

Prefill:    the INITIAL forward pass over the ENTIRE prompt, all
            at once -- highly parallelizable, computed ONCE per
            request

Decode:       GENERATING new tokens ONE AT A TIME (Module 7's
            loop) -- each step depends on the previous step's
            output, inherently SEQUENTIAL

KV cache:       stored Key and Value vectors (Transformers course)
            from all previously-processed tokens, reused instead
            of recomputed at every decode step
Prompt

Prefill                (process the WHOLE prompt, populate the
                       KV cache)

KV Cache

Generate token            (decode step 1 -- uses cached K/V +
                          the new token's Q)

Update cache

Generate next token          (decode step 2 -- reuses cache again)

...

5. How It Works — Step by Step

1. PREFILL: the entire prompt is processed in ONE forward pass --
   computing Key and Value vectors for EVERY prompt token, at
   EVERY layer, and STORING them in the KV cache
2. The FIRST generated token comes from this prefill pass's final
   position's logits (Module 5)
3. DECODE begins: for each new token, compute Q, K, V for ONLY
   the newest token
4. Retrieve ALL previously-cached K, V (no recomputation) and
   combine with the new token's K, V for the attention
   computation
5. Generate the next token, APPEND its K, V to the cache, repeat
6. WITHOUT this cache, every decode step would need to recompute
   K, V for the ENTIRE sequence so far -- exactly the wasteful
   pattern demonstrated in the Transformers course, quantified
   again below specifically for LLM serving

6. Mathematical Intuition

Read the mathematics as a story

trained parameters + prompt → forward passes → generated response

First locate the input, operation, and output. Then treat the formula as a compact description of that journey rather than a collection of symbols to memorize.

Prefill cost scales quadratically with prompt length (attention’s O(n²) cost, Transformers course) — verified directly below.

Decode cost, per step, is O(current_sequence_length) without caching (recomputing everything) but only O(1) new computation per step with caching (only the newest token’s Q/K/V, attending to the already-cached rest) — the same quadratic-vs-linear distinction from the Transformers course, now quantified specifically for a realistic serving scenario.

Analogy: The Restaurant Cooking Pipeline & Pre-chopped Bowls Think of processing prompt text and generating tokens in terms of restaurant logistics:

  • Prefill Phase (The Prep Work): When a table of 8 orders, the chef chops all onions, carrots, and herbs in parallel (the prompt tokens). This takes some effort but is done once per party. The chopped ingredients are placed in bowls on the counter (stored in the KV cache).
  • Decode Phase (Plating Garnish): The chef plates the dishes one by one, adding a final token garnish.
    • Without Caching (Wasteful Chef): For plate 2, the chef throws away all unused prep, reads the recipe, and chops all onions and carrots from scratch again. For plate 3, they chop them all again.
    • With Caching (Efficient Chef): The chef simply scoops from the pre-chopped bowls on the counter (reusing the KV cache), chops only the single new garnish item, and finishes plating instantly.

📊 Visual Flowchart: Prefill vs. Decode Caching Lifecycle

Here is how K and V tensors are calculated once and retrieved dynamically at subsequent steps:

graph TD
    classDef prefill fill:#3498db,stroke:#333,stroke-width:1px,color:#fff;
    classDef decode fill:#2ecc71,stroke:#333,stroke-width:1px,color:#fff;
    classDef cache fill:#f1c40f,stroke:#333,stroke-width:1px,color:#fff;

    subgraph Phase1 ["1. Prefill Phase (Prompt Execution)"]
        Prompt["Input Prompt (100 tokens)"] --> ComputeAll["Parallel forward pass: compute Q, K, V for all 100 tokens"]:::prefill
        ComputeAll --> WriteCache["Write K, V to GPU Memory Cache"]:::cache
    end

    subgraph Phase2 ["2. Decode Phase (Token Generation Steps)"]
        WriteCache --> NextStep["Generate Token step i"]
        NextStep --> NewProj["Compute Q, K, V for the ONE newest token ONLY"]:::decode
        ReadCache["Read previous tokens' K, V from Cache"]:::cache --> Attention["Attention: Q_new @ [K_cached + K_new]"]
        NewProj --> Attention
        NewProj --> UpdateCache["Append newest K, V to Cache"]:::cache
        Attention --> GenWord["Output next token logits"]:::decode
        GenWord --> Loop["Loop back for next step"]
    end

7. Small Worked Example

Walk through the example

  1. Name what each input represents.
  2. Follow one transformation at a time.
  3. Translate the result back into ordinary language.

The purpose is to reveal the mechanism, not merely display an answer.

For a 100-token prompt generating 50 new tokens: without a KV cache, each of the 50 decode steps redundantly recomputes attention over the entire growing sequence (100, then 101, then 102… tokens) from scratch. With caching, each step only computes the newest token’s contribution, reusing everything already computed.


8. Python Example

What the code will demonstrate

The code builds a tiny version of the mechanism, prints values you can inspect, and connects them to the worked example. Predict the direction of the result before running it.

Python symbols used below

  • NumPy (np) stores numeric vectors and matrices.
  • np.array(...) creates a numeric collection.
  • Library calls perform the same conceptual steps shown above at a larger scale.
# Build a small, inspectable example of Inference.
# Follow the inputs, transformations, and output in order.
d_model = 64
seq_lens = [50, 100, 200, 400, 800]

def attention_flops(seq_len, d_model):
    return 2 * seq_len * seq_len * d_model   # core QK^T + softmax@V cost

print("PREFILL cost -- scales QUADRATICALLY:")
for n in seq_lens:
    print(f"  Prompt length {n:4d} -> ~{attention_flops(n, d_model):,} FLOPs")

print(f"\nDoubling 400->800: FLOPs multiplier = {attention_flops(800,d_model)/attention_flops(400,d_model):.1f}x")

# --- DECODE: WITHOUT vs WITH KV cache ---
def decode_flops_no_cache(prompt_len, num_new_tokens, d_model):
    total = 0
    for i in range(num_new_tokens):
        current_len = prompt_len + i + 1
        total += attention_flops(current_len, d_model)   # recomputes EVERYTHING each step
    return total

def decode_flops_with_cache(prompt_len, num_new_tokens, d_model):
    total = 0
    for i in range(num_new_tokens):
        current_len = prompt_len + i + 1
        total += 2 * current_len * d_model   # only new token's Q against cached K
    return total

prompt_len = 100
num_new_tokens = 50
no_cache = decode_flops_no_cache(prompt_len, num_new_tokens, d_model)
with_cache = decode_flops_with_cache(prompt_len, num_new_tokens, d_model)

print(f"\nPrompt length: {prompt_len}, generating {num_new_tokens} new tokens")
print(f"  WITHOUT KV cache: {no_cache:,} total FLOPs")
print(f"  WITH KV cache:    {with_cache:,} total FLOPs")
print(f"  Reduction factor: {no_cache/with_cache:.1f}x")

Expected Output:

PREFILL cost -- scales QUADRATICALLY:
  Prompt length   50 -> ~320,000 FLOPs
  Prompt length  100 -> ~1,280,000 FLOPs
  Prompt length  200 -> ~5,120,000 FLOPs
  Prompt length  400 -> ~20,480,000 FLOPs
  Prompt length  800 -> ~81,920,000 FLOPs

Doubling 400->800: FLOPs multiplier = 4.0x

Prompt length: 100, generating 50 new tokens
  WITHOUT KV cache: 102,134,400 total FLOPs
  WITH KV cache:    803,200 total FLOPs
  Reduction factor: 127.2x

9. How It Works

  • Prefill confirms the Transformers course’s quadratic scaling directly: doubling prompt length from 400 to 800 produces exactly a 4.0x FLOPs increase.
  • Decode without caching costs 102,134,400 FLOPs to generate just 50 tokens after a 100-token prompt — because every single step redundantly recomputes attention over the entire, ever-growing sequence.
  • Decode with caching costs only 803,200 FLOPs for the exact same 50 tokens — a 127.2x reduction. This dramatic gap is why the KV cache isn’t a minor optimization; it’s what makes serving real, multi-token generation requests economically and practically feasible at all.

10. Why Generation Is Expensive

1. Prefill's quadratic cost with prompt length (verified directly)
2. Decode's inherently SEQUENTIAL nature (Module 7) -- each step
   must wait for the previous one, limiting parallelization within
   a single request
3. EVEN WITH caching, growing KV cache consumes real, growing GPU
   memory (Transformers course) as context length increases

11. Latency, Throughput, and Batch Inference

Latency:            time to get a response for ONE request

Throughput:            total tokens generated across ALL
                    concurrent requests, per unit time

Batch inference:         processing MULTIPLE requests together,
                      exploiting shared GPU compute more
                      efficiently -- improves throughput, can
                      affect individual-request latency

These are genuinely distinct, sometimes competing, production metrics — optimizing purely for one can hurt the other (Module 24 covers specific techniques like continuous batching that help balance both).


12. How Is This Used in Modern AI?

Trace it through a real model call

user message → assembled context → LLM computation → decoded output → application checks

This topic affects one stage of that path; it is not the complete product. Hosted GPT- and Gemini-style applications also add instructions, safety systems, retrieval, tools, serving infrastructure, and evaluation around the model.

🤖 How Is This Used in Modern AI?

Every production LLM serving system implements prefill/decode as structurally distinct phases with KV caching as standard, essential infrastructure — not an optional feature. The dramatic reduction verified directly (127x for a modest example) is precisely why no serious production system operates without it.


13. How Is This Used in Agentic AI?

Separate the model from the runtime

goal + state + tool results → LLM proposal → runtime validation → execution or response

The LLM proposes text or a structured action. Ordinary application code controls permissions, tools, retries, memory, and execution.

Direct relevance to Agentic AI: Very High. Long agent conversations with growing context (system prompt, history, tool results) directly stress both prefill cost (reprocessing a growing prompt on each new turn, unless the KV cache can be reused across turns) and the KV cache’s memory footprint.

Understanding this is essential for reasoning about why long-running agent sessions have real, growing latency and cost characteristics.


14. Common Beginner Mistakes

⚠️ Mistake

Incorrect idea: treating prefill and decode as the same computational problem.

Why it is incorrect: As demonstrated, they have fundamentally different cost structures — prefill is a one-time, highly parallel, quadratic-cost operation; decode is repeated, sequential, and (with caching) incrementally linear-cost.

⚠️ Mistake

Incorrect idea: assuming KV caching is a minor optimization.

Why it is incorrect: As verified directly with a 127x reduction, it’s the difference between a practically-servable system and a computationally catastrophic one.

⚠️ Mistake

Incorrect idea: conflating latency and throughput.

Why it is incorrect: They’re genuinely different metrics that can trade off against each other — a system optimized purely for throughput (via aggressive batching) may show worse individual-request latency.


15. Important Distinctions

PrefillDecode
Processes the ENTIRE prompt at onceGenerates ONE token at a time
Highly parallelizableInherently sequential
Quadratic cost with prompt length — verified directlyLinear cost per NEW token, WITH caching — verified directly
LatencyThroughput
Time for ONE requestTotal tokens across ALL concurrent requests, per unit time

16. When to Use

KV caching should always be used in any real LLM serving deployment — there’s no legitimate reason to omit it given the dramatic, verified cost difference. Batch inference should be used when throughput across many concurrent requests matters more than any single request’s absolute minimum latency.


17. When Not to Use

Not applicable for KV caching — it’s standard, essential infrastructure. Heavy batching may be inappropriate for latency-critical, single-user interactive applications where consistent, low per-request latency matters more than aggregate throughput.


18. Production Considerations

  • KV cache memory grows with context length — a genuine, real constraint on how many concurrent requests (and how long a context) a given amount of GPU memory can support (Module 24 covers techniques like GQA/MQA that reduce this further, per the Transformers course).
  • Prefill and decode often have different optimal batching strategies in real serving systems, given their different computational characteristics.
  • Long agent conversations’ repeated prefill cost (if context can’t be cached across turns) is a genuine, practical latency and cost concern worth architecting around.

19. What You Should Remember

  • Prefill processes the whole prompt once, with quadratic cost in prompt length — verified directly (4x cost for 2x length).
  • Decode generates tokens one at a time, sequentially — verified directly to cost 127x more without KV caching than with it, for a realistic example.
  • Latency and throughput are distinct, sometimes competing production metrics — batch inference trades between them.

20. Interview Questions

Beginner

Q: What is the difference between the prefill and decode phases of LLM inference?

Ans: Prefill is the initial processing of the entire prompt in one parallel forward pass, computing and caching Key/Value vectors for every prompt token.

Decode is the subsequent, sequential generation of new tokens one at a time, each step reusing the cached information from prefill (and previous decode steps) rather than reprocessing everything from scratch.

Intermediate

Q: Why does KV caching matter so much for decode-phase efficiency?

Ans: Without caching, generating each new token would require recomputing attention over the entire, ever-growing sequence from scratch — a massively redundant computation, since earlier tokens’ Key and Value vectors never change once computed (a direct consequence of causal masking, Module 11).

Verified directly in this module: caching reduced total decode FLOPs by over 127x for a realistic 50-token generation scenario, turning an increasingly expensive quadratic-per-step cost into a much cheaper, roughly linear one.

Advanced

Q: Why does prefill cost scale quadratically with prompt length while KV-cached decode cost scales roughly linearly per new token?

Ans: Prefill computes the full attention score matrix across all n prompt tokens at once — an n × n computation, hence quadratic in prompt length, verified directly (doubling length produced a 4x FLOPs increase).

Decode, with caching, only computes the NEW token’s Query against the already-cached Keys of all previous tokens — a computation proportional to the current sequence length, not squared, since the n × n full-matrix computation isn’t repeated; only one new row is added each step. This structural difference is exactly why the two phases have such different serving cost profiles.

Scenario

**Q: A team’s LLM-powered application shows high, growing latency specifically for very long conversations, even though individual messages are short.

Using this module, what would you investigate?** A: I’d investigate two things directly connected to this module: first, whether the KV cache is being reused efficiently across conversation turns, or whether the system is unnecessarily reprocessing (re-prefilling) the growing conversation history on every turn — a genuine, avoidable cost.

Second, I’d check whether growing KV cache memory usage (which scales with total context length) is causing memory pressure or forcing less efficient batching as the conversation grows — both are real, expected consequences of longer contexts under this module’s cost model, not signs of an unrelated bug.

AI Engineering

Q: Why do production LLM serving systems treat prefill and decode as architecturally distinct phases, sometimes even serving them on different infrastructure?

Ans: Because they have genuinely different computational characteristics and optimization needs — prefill is a one-time, highly parallel, compute-intensive operation per request, while decode is a repeated, inherently sequential, comparatively lighter-weight-per-step operation (especially with KV caching).

Some advanced serving architectures separate these phases (sometimes called “disaggregated” prefill/decode serving) specifically to optimize each independently — allocating resources suited to each phase’s distinct cost profile, rather than treating the entire request as one undifferentiated computation.

21. Next Step

Next: Module 15 — Temperature, Top-K and Top-P — precisely how a token gets selected from the probability distribution (Module 5), and how each sampling parameter changes generation.

Author
TechByteByByte Editorial Team
Reviewed by
TechByteByByte Admin
Published
Last reviewed