Part IV ยท Ch. 18 โ€” Inference, Efficiently

Part IV ยท Chapter 18 of 20

Inference, Efficiently

KV cache, context windows, and quantization


The pause and the stream

Paste three pages into a chatbot and hit send. First there is a pause โ€” a second or two of nothing, a held breath. Then the reply arrives at a steady clip: word, then word, then word, marching out at a pace you can almost read along with. Two completely different speeds, and you get them every single time. By the end of this chapter you'll know exactly what happens in that pause, why the streaming afterward is so steady, and why neither one is an accident.

Everything so far has been about how the machine is built (Part II) and how it learns (Part III). Today is about running it โ€” inference. During inference nothing learns and no weight moves so much as a hair; the entire game is doing one fixed computation fast and cheap. There is no new math in this chapter โ€” it is engineering, built entirely out of tools you already own: the generation loop, the dot product, softmax, the causal mask.

Here is the itinerary in one sentence: what a single token costs to produce, the cache that refuses to compute anything twice, what a context window actually is (and where its teeth are), and how a weight like 0.34 survives being stored as "shelf 3" in four bits.

What one token costs

Recall the loop from Chapter 9: generation is sample → append → run again. Every generated token means one full trip through the tower โ€” embeddings in, all $L$ layers of attention-and-MLP, unembedding out. And here is the sentence that should sting a little: for every one of those trips, essentially every learned number in the model gets read and used. Billions of weights, touched per token. That is the baseline bill, and it is exactly why "tokens per second" is the universal speed metric for serving an LLM โ€” the whole machine, once, per word.

Now let's find the waste. Walk the site's running sentence one step. The model has processed "the cat sat" and predicted "on" โ€” this is Chapter 9's very sentence, caught mid-flight. We append "on." Naively, we now re-run the whole stack on all four tokens, recomputing everything for "the," "cat," and "sat" from scratch. But look at what the causal mask (Chapter 10) guarantees: "the," "cat," and "sat" were never allowed to peek forward, so nothing about the new arrival can change any of their computed vectors. Their queries, their keys, their values โ€” everything, in every layer โ€” come out bit-for-bit identical to last time.

Say the conclusion plainly, because a whole industry rests on it: at each generation step, the only genuinely new work is the newest token's. Everything computed for earlier tokens is still valid, sitting there unchanged. Recomputing it is pure, provable waste โ€” and the fix is the most-used piece of engineering in all of LLM serving.

The KV cache: never compute twice

The fix is one sentence long, and then we'll work it by hand. Keep a running store of every past token's key vector and value vector, in every layer โ€” the KV cache โ€” so a new token computes only its own three role-vectors and reads everyone else's from the store. Why keys and values but not queries? A token's query is used once, the moment it asks its question; its key and value are what every future token will need to score it and mix it in. Past questions are spent. Past answers stay on file.

Let's work one full step with Chapter 10's exact cast. After "the cat sat," the cache holds six little vectors (one toy head, one layer): the keys $\mathbf{k}_{\text{the}} = [1, -1]$, $\mathbf{k}_{\text{cat}} = [1, 1]$, $\mathbf{k}_{\text{sat}} = [0, 1]$, and the values $\mathbf{v}_{\text{the}} = [0, 2]$, $\mathbf{v}_{\text{cat}} = [3, 1]$, $\mathbf{v}_{\text{sat}} = [1, 0]$ โ€” Chapter 10's numbers, unchanged. Now "on" arrives with its own freshly computed trio, invented here for this chapter's toy (same pretend mini-model as Chapter 10): $\mathbf{q}_{\text{on}} = [2, 1]$, $\mathbf{k}_{\text{on}} = [1, 0]$, $\mathbf{v}_{\text{on}} = [0, 1]$. The only attention arithmetic needed this step is on's query against the four keys on file. Here is the loudest one worked in full:

$$\mathbf{q}_{\text{on}} \cdot \mathbf{k}_{\text{cat}} = 2 \times 1 + 1 \times 1 = 3$$

In words: line on's question up against cat's key, multiply matching slots, add โ€” a score of 3.

Worked example: on's four scores

$\mathbf{q}_{\text{on}} \cdot \mathbf{k}_{\text{the}} = 2 \times 1 + 1 \times (-1) = 1$;  $\mathbf{q}_{\text{on}} \cdot \mathbf{k}_{\text{cat}} = 2 \times 1 + 1 \times 1 = 3$;  $\mathbf{q}_{\text{on}} \cdot \mathbf{k}_{\text{sat}} = 2 \times 0 + 1 \times 1 = 1$;  $\mathbf{q}_{\text{on}} \cdot \mathbf{k}_{\text{on}} = 2 \times 1 + 1 \times 0 = 2$.

Four scores: $(1,\, 3,\, 1,\, 2)$. Not one earlier vector was recomputed to get them โ€” the keys came straight off the shelf.

Now soften the scores into shares with Chapter 9's softmax โ€” unscaled, the same convention as Chapter 10's spine. Exponentiate each score, then divide by the total. Every exponential comes from Chapter 4's table or its multiply rule ($e^{3} = e^{1} \cdot e^{2} = 2.72 \times 7.39$): $e^{1} \approx 2.72$, $e^{3} \approx 20.09$, $e^{1} \approx 2.72$, $e^{2} \approx 7.39$, totaling $\approx 32.92$:

$$(a_{\text{the}},\, a_{\text{cat}},\, a_{\text{sat}},\, a_{\text{on}}) = \mathrm{softmax}(1,\, 3,\, 1,\, 2) = \left(\tfrac{2.72}{32.92},\, \tfrac{20.09}{32.92},\, \tfrac{2.72}{32.92},\, \tfrac{7.39}{32.92}\right) = (0.08,\, 0.61,\, 0.08,\, 0.22)$$

In words: turn the four scores into four shares of on's attention. We reuse Chapter 10's symbol $a_i$ for an attention share โ€” "a," not $w$, because these are computed on the fly, not learned. So "on" leans hard on "cat" (0.61), gives a fifth to itself (0.22), and spares a little for "the" and "sat" (0.08 each). One honest note, said once: rounded to two decimals these shares read $0.08 + 0.61 + 0.08 + 0.22 = 0.99$ โ€” the familiar two-decimal artifact from Chapter 10, not a bug.

Worked example (optional): the value mix

The output is the shares blending the values on file, exactly Chapter 10's move:

$$\mathbf{o}_{\text{on}} = 0.08\,[0, 2] + 0.61\,[3, 1] + 0.08\,[1, 0] + 0.22\,[0, 1] = [1.91,\, 0.99]$$

In words: scale each stored value vector by its share and add them slot by slot. Slot 1: $0.08 \times 0 + 0.61 \times 3 + 0.08 \times 1 + 0.22 \times 0 = 1.91$. Slot 2: $0.08 \times 2 + 0.61 \times 1 + 0.08 \times 0 + 0.22 \times 1 = 0.99$. "on" leans hard on "cat" โ€” reasonably enough, since cats sit on things. Finally, file $\mathbf{k}_{\text{on}}$ and $\mathbf{v}_{\text{on}}$ into the cache, ready for the next arrival.

Count what the cache just saved. Without it, this step recomputes the whole $4 \times 4$ score grid โ€” and re-derives every query, key, and value behind it, in every layer. With it, attention does one new row: on's query against four keys. The three earlier rows were never touched today.

A 4-by-4 attention-share grid in the same style as Chapter 10's heatmap, rows labeled 'asking' (the, cat, sat, on) and columns labeled 'being scored' (the, cat, sat, on), mint cells brighter for larger shares. The upper-right triangle above the diagonal is blanked to dark panels with muted em-dashes (the causal mask). Rows one to three (the, cat, sat) are dimmed to about a third opacity, with a slim muted bracket beside them labeled 'computed in earlier steps โ€” read from cache, never redone'. The bottom row (on) renders at full strength with values 0.08, 0.61, 0.08, 0.22 and is outlined by a bright mint rounded rectangle, annotated 'the only new work this step'.
One token, one new row. When "on" arrives, the causal mask guarantees the three earlier rows come out identical to last time โ€” so with a KV cache they are never recomputed (dimmed). The machine computes one fresh row: on's query against every key on file, giving shares 0.08 / 0.61 / 0.08 / 0.22 (two-decimal rounding makes this row read 0.99). Without the cache, every step would rebuild the whole grid.

At toy scale the saving looks modest. At real scale it is brutal: at position 1,000, the cache turns roughly 1,000 rows of redone work into 1.

And now we can cash in the hook. The pause before the first word is prefill: your entire prompt sweeps through the stack in one parallel pass โ€” all positions at once, the transformer's native talent (Chapter 11) โ€” filing keys and values for every prompt token into the cache. The stream afterward is generation: one token, one new row, against a full cache โ€” steady, predictable work per token. Two phases, two speeds.

A left-to-right timeline in two phases split by a vertical hairline. Left phase, labeled 'prefill โ€” the pause': five token chips packed tightly, with one wide blue bracket beneath them labeled 'processed together, one parallel pass', and an arrow down into a cache drum (a cylinder labeled 'KV cache, keys plus values per layer') that shows five mint tick squares inside. Right phase, labeled 'generation โ€” the stream': three token chips appearing one at a time with tick labels t+1, t+2, t+3, each with a thin arrow down into a second cache drum showing six, seven, then eight mint squares, annotated in mint 'each new token: compute 1, read the rest'. Bottom caption: 'same machine, two speeds โ€” a big parallel gulp, then steady sips'.
Why every reply has two speeds. During the pause (prefill), the whole prompt sweeps through the stack in one parallel pass, filing every token's keys and values into the KV cache. During the stream (generation), each new token computes only its own vectors and reads everyone else's from the cache โ€” one steady, predictable sip of work per word.

What a context window really is

Define it with the tools already on the table. The context window is the largest $n$ โ€” $n$, the number of tokens in context, as always โ€” that the machine can hold and attend over at once. It is a workspace, not a memory and not knowledge. The model's knowledge lives in its frozen weights (Chapter 11); the window holds the tokens of the current job, plus their cached keys and values. A bigger window is a bigger desk, not a smarter worker.

Why is it finite? Two honest reasons. Reason one โ€” training territory: the model only ever practiced on sequences up to some length, and its seat stamps (the positional information from Chapter 11) beyond that range describe terrain training never visited; models behave unreliably off the map. Reason two โ€” cost, and here your own numbers bite. Attention compares every asker with every earlier token, so the prefill score work grows with the square of the length. Chapter 10 planted this seed; let's work it:

$$n = 1{,}000 \;\rightarrow\; n^2 = 1{,}000{,}000 \qquad n = 10{,}000 \;\rightarrow\; n^2 = 100{,}000{,}000$$

In words: ten times the context means a hundred times the score grid โ€” per layer, per head. That is the quadratic tooth.

The cache is gentler, but still relentless: it grows linearly with $n$, because every token adds its one key and one value, in every layer. One real-scale computation, every factor from Chapter 11's public GPT-3 numbers (embedding width 12,288; 96 layers): $2 \text{ vectors} \times 12{,}288 \text{ numbers} \times 96 \text{ layers} \approx 2.4$ million numbers per token โ€” about 4.7 MB at 2 bytes per number. A full 2,048-token context is therefore roughly 10 GB of cache, before you store the model itself. When you hear that long-context serving is a memory problem, this is the memory.

A line chart. X axis: context length n in tokens from 0 to 10,000. Y axis: relative cost, normalized at n equals 1,000, from 0 to about 105. An amber curve rising steeply as n squared is labeled 'prefill scores โ€” grows as n squared', with two amber dots annotated '1,000 tokens gives 1,000,000 scores' near the bottom and '10,000 tokens gives 100,000,000 scores' near the top. A blue straight line rising gently is labeled 'KV-cache memory โ€” grows as n'. A muted footnote reads 'per layer, per head โ€” the shapes are the story, not the units'.
Why context length has teeth. Ten times the tokens means ten times the cache memory (blue, linear) but a hundred times the prefill score grid (amber, quadratic โ€” $n^2$ dot products, per layer, per head). Long windows are bought with engineering on both curves, and every window still ends somewhere.

What happens when a conversation outgrows the window? A practical truth, no new mechanism: the app โ€” not the model โ€” must drop or summarize older turns before re-sending them. The model has no idea anything is missing, because for the model there is no "anything" outside its context. Real windows have grown from 2,048 tokens in the GPT-3 era to hundreds of thousands today โ€” hard engineering on every front of this chapter โ€” but every window still ends somewhere, and the quadratic teeth still bite.

Quantization: shrinking the numbers

Third pressure point: the weights themselves. Computers store each number with a fixed budget of on/off switches โ€” bits โ€” and the budget sets how fine a ruler you get. 32 bits distinguishes billions of values; 4 bits distinguishes exactly 16. Standard training uses 32- or 16-bit weights, so a 7-billion-weight model is 28 or 14 GB before it computes a single thing. The size problem is not the count of weights โ€” it is the luxury of each one.

Quantization is the decision that weights don't need luxury. Here is the chapter's worked spine. Take four real weights from one row of a grid: $[0.34,\, -0.12,\, 0.7,\, 0.23]$. Now build the 4-bit ruler, and notice where its one magic number comes from. Four bits give us sixteen patterns to spend. Spend them as the whole numbers $-7$ through $+7$ โ€” that is fifteen shelves, sign included, with one pattern left over โ€” and the biggest shelf number available to us is 7. So we size the ruler by finding the biggest magnitude among our weights (0.7) and dividing it by that 7, which gives the shelf spacing โ€” the scale. Shelves land at $0, \pm 0.1, \pm 0.2, \dots, \pm 0.7$, and the largest weight sits exactly on the top shelf, which is the point: stretch the ruler to fit the widest number we actually have:

$$\text{scale} = \frac{0.7}{7} = 0.1 \qquad 0.34 \;\rightarrow\; \text{store } 3 \;\rightarrow\; 3 \times 0.1 = 0.30$$

In words: store each weight as its nearest shelf number โ€” a small whole number that fits in 4 bits โ€” and to use it later, multiply the shelf number back by the scale. So 0.34 rounds to shelf 3, and rebuilding gives $3 \times 0.1 = 0.30$, a little shy of the original.

Worked example: the round-trip, all four weights

Nearest shelf numbers: $[0.34,\, -0.12,\, 0.7,\, 0.23] \rightarrow (3,\, -1,\, 7,\, 2)$. Rebuild each by multiplying back by the scale 0.1:

$3 \times 0.1 = 0.30$ (was 0.34);  $-1 \times 0.1 = -0.10$ (was −0.12);  $7 \times 0.1 = 0.70$ (exact);  $2 \times 0.1 = 0.20$ (was 0.23).

Three horizontal bands. Top band: a muted number line from minus 0.7 to plus 0.7 with tick marks every 0.1, labeled minus 0.7, 0, plus 0.7, and shelf numbers minus 7 to 7 beneath each tick; a title reads 'the 4-bit ruler โ€” 16 possible stored values, spacing = scale = 0.1'. Middle band: four blue dots above the line at 0.34, minus 0.12, 0.70, and 0.23 with blue labels, each dropping a thin arrow to its nearest shelf tick, landing on a mint square at 0.3, minus 0.1, 0.7, 0.2, with mint stored values 3, minus 1, 7, 2 beneath. Bottom band: two pill rows, '32-bit: 0.3400000035โ€ฆ (4 bytes each)' and '4-bit: shelf 3, ruler times 0.1 (half a byte each)', joined by an arrow.
Quantization is a coarse ruler. Pick a scale (here $0.7/7 = 0.1$), then store each weight as its nearest shelf number: 0.34 becomes shelf 3, −0.12 becomes shelf −1. Rebuilding gives 0.30 and −0.10 โ€” a little off, each way, at random โ€” and the model's big blended sums barely notice. Half a byte per weight instead of four: an eight-fold smaller model.

Why doesn't the model fall apart? Test it on the operation that actually matters โ€” Chapter 2's dot product, the machine's heartbeat. Dot both versions of our row against $\mathbf{x} = [1, 1, 1, 1]$: the true weights give $0.34 - 0.12 + 0.7 + 0.23 = 1.15$; the shelf weights give $0.3 - 0.1 + 0.7 + 0.2 = 1.10$. Off by about 4% โ€” and this is a four-number sum. Real sums run over thousands of weights whose rounding errors point up and down at random, and many small random errors in a big sum behave like Chapter 6's coin flips: some land high, some land low, and the total hugs the middle rather than running away. The model never needed the third decimal of any single weight; it needs the big blended sums to come out right โ€” and they do.

One more honest reason it works: training itself was noisy โ€” every batch of examples jiggled the weights a little (Chapter 15) โ€” so the final function settles into a comfortably wide valley in the loss landscape (Chapter 13's language), not balanced on a knife's edge. In a wide valley, small nudges to the weights barely move the outputs. Quantization is one more small nudge.

The fine print, one beat, honestly. Real schemes don't share a single ruler across a whole grid โ€” they store one full-precision scale per small group of weights (each block of, say, 32 or 64 gets its own ruler), which keeps the shelves snug everywhere. And quantization is not free: push down to 4 bits and quality dips a little; push to 2 and models get noticeably worse. The sweet spot in the wild sits around 4 to 8 bits. Then the payoff, which is pure arithmetic โ€” bits times count:

$$28\ \text{GB} \;(32\text{-bit}) \;\rightarrow\; 14\ \text{GB} \;(16\text{-bit}) \;\rightarrow\; 7\ \text{GB} \;(8\text{-bit}) \;\rightarrow\; 3.5\ \text{GB} \;(4\text{-bit})$$

In words: four bits instead of thirty-two is an eight-times smaller model โ€” the difference between a server rack and the phone in your pocket.

Why the LLM cares

This chapter is the decoder ring for every model card and pricing page you will ever read. Practice on a real-looking spec string: "7B-Q4, 8K context." You can now read every piece of it โ€” 7 billion parameters (Chapter 11's counting), quantized to 4 bits (~3.5 GB, small enough to run locally), and a context window of 8,192 tokens (its whole workspace, cache costs and all). Nothing in the string is mysterious anymore.

Now decode the everyday experiences, one clause each. The pause before the first word is prefill filling the cache. The steady streaming is one new row per token. Long conversations slowing down and costing more is the row lengthening and the cache swelling with $n$. Input tokens priced cheaper than output tokens is typical practice with an honest mechanism: prefill digests your whole prompt in one parallel pass, while output tokens are minted one serial trip at a time. And "this model runs on a phone" โ€” quantization did that.

One breadth beat, names only, no mechanics. Serving stacks pile on further tricks in the exact same spirit as today. Batching pushes many users' requests through the machine at once (it is why shared APIs are cheap). Speculative decoding has a small, fast model draft several tokens and lets the big model check the whole draft in one pass. Every one of them obeys the same rule: never do expensive work you can avoid, skip, or share.

Connect it backward and forward. The KV cache is Chapter 10's grid made affordable; quantization is Chapter 2's dot products proving robust; the context window is the honest boundary around it all. And that boundary sets up a real problem: your documents, your company wiki, your lab notebooks will never fit in any window. The fix is not a bigger desk โ€” it is a library card. That's next.

What you now know

  • Inference is running the frozen machine: every generated token takes one full trip through the stack, touching essentially every weight โ€” which is why speed is measured in tokens per second.
  • The causal mask means earlier tokens' computations never change when a new token arrives, so the KV cache files every past key and value (per layer) and each new token computes only its own row โ€” on's query against the keys on file gave shares 0.08 / 0.61 / 0.08 / 0.22 with one row's work instead of four.
  • Every reply has two phases: prefill (the pause โ€” the whole prompt in one parallel pass, filling the cache) and generation (the stream โ€” one steady row per token).
  • The context window is workspace, not knowledge: it is finite because training never visited longer sequences and because costs bite โ€” prefill scores grow as $n^2$ (1,000 tokens → 1,000,000 scores; 10,000 → 100,000,000) while cache memory grows with $n$ (roughly 4.7 MB per token at GPT-3 scale).
  • Quantization stores each weight as a shelf number on a coarse ruler โ€” $[0.34, -0.12, 0.7, 0.23]$ became $(3, -1, 7, 2)$ at scale 0.1 โ€” and the model survives because dot-product sums average away small random rounding errors, taking a 7B model from 28 GB to about 3.5 GB.
  • You can now read a model card โ€” "7B-Q4, 8K context" โ€” and every experience it implies: the first-word pause, the steady stream, the long-chat slowdown, and why the thing runs on a phone.

Where we're headed. The context window is honest about its limits: it is a desk, not a library, and your company wiki, your lab notebooks, and last month's meeting notes will never fit on it. Fine-tuning (Chapters 16–17) is the wrong tool too โ€” weights are for skills, and they make an expensive, unreliable filing cabinet for facts that change weekly. The right move is older than computers: don't memorize the library โ€” get a library card. Next chapter, Chapter 8's embedding map goes industrial: every passage you own becomes an arrow, a question becomes an arrow, and the nearest arrows get pulled off the shelf and placed on the desk โ€” right inside the context window you now understand โ€” just in time for the model to read them. It's called retrieval-augmented generation, and it is the last new machine this site builds.