~/fba-lab/lab/qwen-c/10-prefill-decode

FBALab

Code · architecture · study mode

AboutRoadmapSpeedrun
FBALab

Study mode — no GPU required.

Interactive LLM training & inference lab.

Qwen CAboutContactTermsPrivacyCookiesCommunity

© 2026 FBA Lab

Contact · contact@bubblspace.com · +91 75061 55016

INFERENCE SIMULATION

Qwen3 Pure C — 10 · Prefill vs decode

running
◷run_viz.c▸Prefill vs decode — one branch◎learner⌁10-prefill-decode$no GPU
weights
Load the model
The 4 GB weight file is mapped into memory in milliseconds. No copying — the OS pages in only what inference needs.
run_viz.c · stage 01–02
load
runtime
Run inference
Your prompt flows through 28 layers of attention and computation, one token at a time.
stage 03–04
forward
output
Generate text
The model scores all 151,936 possible next words and picks one. Then repeats.
stage 05–06
1/3
Blocks
Quick summary

This if/else is the entire distinction between reading the prompt (prefill) and generating new text (decode) — it just chooses where the next token comes from.

Full explanation below the code →

fba-lab — run_viz.c · Prefill vs decode — one branchexecuting
// block: Prefill vs decode — one branch · lines 1067–1071$ study run_viz.c --block pd_branchThis if/else is the entire distinction between reading the prompt (prefill) and generating new text … ✓
Explanation

There is no separate prefill() and decode() in this codebase. Both phases run the same forward(); the only difference is where the input token comes from, decided by this one branch (lines 1067–1071):

- While pos < num_prompt_tokens — we are still inside the prompt, so feed the next prompt token (line 1068). This is prefill: teacher-forcing the model with the known input to build up the KV cache.

- Once pos reaches the prompt length — the prompt is consumed, so feed the model's own previously generated token, next (line 1070). This is decode: autoregressive generation.

So "prefill" here is a *phase*, not a batched matrix operation. The prompt tokens are pushed through one at a time, exactly like decode — each is a single-vector forward(), i.e. a GEMV. The KV cache fills up token by token either way.

Why it matters

The lesson's prefill exists as a phase but is still token-by-token GEMV. Production engines turn that same phase into one big GEMM by packing all prompt tokens into a matrix — the main reason production prefill is so much faster than looping one token at a time.

Think about

In this code, is prompt processing (prefill) a GEMV or a GEMM operation, and how does that differ from a production engine?

// architecture

Live diagram

100%
if (pos < num_prompt_tokens)token = prompt[pos] ← prefillelsetoken = next ← decode (own token)positions →t0t1t2t3t4PREFILL — prompt tokenst5t6t7DECODE — generatedeach pos: forward() writes K/V into the cache slot, then sample() → nexttoken → forward(pos) → logits → sample → next → (feeds pos+1)this port: prefill looped one-at-a-time = GEMVproduction: prompt packed into a matrix = GEMMdecode: one query vs KV cache = GEMV (unless batched)
← 9 · Attention & memoryOpen live sampling viz →
Step 10 of 10Skill: Inference Systems
01Blueprint02Load weights03Buffers04Forward pass05Tokenizer06Generate loop07GEMV, not GEMM08Every op a kernel09Attention & memory10Prefill vs decode