← all writing

The KV Cache: Why It's Allowed, and What It Costs

Part 1 of 4 on how a transformer remembers.KV cache · Linear attention · Kernelization · Recurrent memory

A language model writes one token at a time. Taken literally, that should be catastrophic: to produce the 500th token you’d re-read the previous 499, and to produce the 501st you’d re-read 500. Generation would be quadratic in its own output length.

It isn’t, because of one table. This post is about why that table is allowed to exist, exactly what goes into it, and the bill it quietly runs up. Every diagram below is animated — press play.

《transformer 怎么记东西》四篇之一。KV 缓存 · 线性注意力 · 核化 · 递归记忆

语言模型一次只写一个 token。照字面理解,这该是灾难性的:要产出第 500 个 token,你得把前面 499 个重读一遍;要产出第 501 个,再重读 500 个。生成的代价会是自己输出长度的平方。

但它不是,全靠一张表。这篇讲的就是:这张表凭什么能存在、里面到底装了什么、以及它在背后悄悄记的那笔账。下面每一张图都是动画——按播放。

Only two of the three

Every attention head projects every token three ways — into a query, a key, and a value. Only two of the three ever get cached, and the name tells you which. Worth asking why, because the answer turns out to be the cleanest door into everything else.

Here is one head at position $t$:

\[o_t \;=\; \mathrm{softmax}\!\left(\frac{q_t K^{\top}}{\sqrt{d}}\right) V\]

Ignore the operation for a second and look at the shapes. $q_t$ is a single row — this token’s query, and nothing else’s. $K$ and $V$ are stacks: the keys and values of every token so far, all of them, needed in full, at every step.

So a decoding step consumes one query and the entire history of keys and values. Which means $q_t$ gets read exactly once — at the step that built it — while $k_i$ gets read at step $i$, and $i+1$, and $i+2$, and every step until the sequence ends.

A cache only pays for itself on something you read more than once. Watch the counters.

三个里面,只缓存两个

每个注意力头都把每个 token 投影成三样东西——query、key、value。但三个里面只有两个会被缓存,而它的名字已经告诉你是哪两个。这个「为什么」值得问,因为答案恰好是理解后面一切最干净的入口。

一个头在位置 $t$ 上做的事:

\[o_t \;=\; \mathrm{softmax}\!\left(\frac{q_t K^{\top}}{\sqrt{d}}\right) V\]

先别看运算,看形状。$q_t$ 只有一行——当前这个 token 的 query,别人的都不是。$K$ 和 $V$ 是两摞:到目前为止每一个 token 的 key 和 value,一个不少,每一步都要用全。

所以一个解码步吃掉的是一个 query 和整段历史的 key、value。这意味着 $q_t$ 只会被读一次——就在造出它的那一步——而 $k_i$ 会在第 $i$ 步被读,第 $i+1$ 步、第 $i+2$ 步,直到序列结束的每一步都还要被读。

缓存只有对被读不止一次的东西才回得了本。盯着那些计数器看。

So the cache holds what the new row has to look at, not the row itself. Queries are scaffolding — built, fired, discarded. Keys and values are the record.

What that one row actually does

“q is one row” is easy to say and easy to skip past, so here is the row doing its job, number by number. There is nothing in it beyond a dot product, a softmax, and a weighted average — but watching where each value comes from is what makes the rest of this post concrete.

所以缓存存的是新的那一行要去看的东西,而不是那一行本身。Query 是脚手架——造出来、开一枪、扔掉。Key 和 value 才是那份记录。

这一行到底在做什么

「q 只有一行」这句话很好说,也很容易一眼扫过去,所以下面把这一行干活的过程一个数一个数地放出来。里面除了一个点积、一个 softmax、一次加权平均之外没有别的东西——但看清每个数字是从哪儿来的,后面整篇才会变得具体。

What actually goes into the two tables

So much for what the rows are for. Where do they come from? Watch a sentence turn into them.

那两张表里到底装了什么

这些行拿来干什么讲完了。那它们是从哪来的?看一句话怎么变成它们。

Why you’re allowed to keep it

Here’s the part that usually gets skipped. Caching a computation is only valid if the cached value can’t change. So why can’t $k_3$ change?

Because of the direction of the arrows. In a causal model, the hidden state $h_i$ is built from tokens $1\ldots i$ and nothing else — the attention mask forbids position $i$ from looking right. And $k_i = h_i W_K$ is a per-position map. So the moment token 3 has been seen, $k_3$ and $v_3$ are final. No future token can reach back and perturb them.

The same causality gives you the second half: appending a token doesn’t change any earlier output either, because query $t$ never attended to anything past $t$ in the first place. Old rows of the attention matrix are untouched, bit for bit.

Toggle the mask off in the panel below and watch both properties die at once.

它凭什么能被留下来

这一步通常被跳过。缓存一个计算结果,只有在这个结果不会变的前提下才合法。那么,$k_3$ 为什么不会变?

因为箭头的方向。在因果模型里,隐状态 $h_i$ 只由 token $1\ldots i$ 构成,别的什么都没有——注意力掩码禁止第 $i$ 个位置往右看。而 $k_i = h_i W_K$ 是一个逐位置的映射。所以第 3 个 token 一被看到,$k_3$ 和 $v_3$ 就定稿了。未来的任何 token 都没法反过来扰动它们。

同一个因果性还送你另一半:追加一个 token 也不会改变任何更早的输出,因为 query $t$ 本来就没看过 $t$ 之后的东西。注意力矩阵的旧行原封不动,逐比特相同。

把下面面板里的掩码关掉,看这两个性质怎么同时死掉。

This is also the cleanest way to see why BERT-style bidirectional models can’t stream. There, every token sees every other token, so adding one word at the end rewrites every representation in the sentence. Nothing is final until the whole input is final, and a cache of “final” values has nothing to hold. Autoregressive generation and the KV cache are the same design decision, seen from two sides.

Prefill and decode are different machines

Because the prompt is known all at once, its rows don’t have to be built one at a time. That splits inference into two phases with completely different characters.

Prefill runs the whole prompt in one pass. Every prompt token’s $k$ and $v$ is independent of the others, so the GPU does it as one large matmul. Lots of arithmetic per byte of weights moved — the hardware is busy.

Decode then goes one token at a time, and each step has to attend over everything already in the cache. The arithmetic is tiny — one query row — but the entire cache gets pulled out of memory again. The hardware mostly waits.

这也是理解「为什么 BERT 那类双向模型没法流式输出」最干净的角度。在那里,每个 token 都能看到其它所有 token,所以在末尾多加一个词,整句话的表示全部要重写。在整个输入定稿之前,没有任何东西是定稿的,而一个存「定稿值」的缓存也就无物可存。自回归生成和 KV 缓存,是同一个设计决定的两面。

Prefill 和 decode 是两台不同的机器

因为提示词是一次性给全的,它那些行不必一行一行地造。这就把推理劈成了性格完全不同的两个阶段。

Prefill 把整段提示词一次跑完。每个提示词 token 的 $k$、$v$ 互不依赖,所以 GPU 把它当一个大矩阵乘做掉。每搬运一个字节的权重能摊到很多次算术——硬件是忙的。

Decode 接着一个 token 一个 token 地走,而每一步都得和缓存里已有的全部做注意力。算术量很小——就一行 query——但整个缓存要从显存里再拉一遍。硬件大部分时间在等。

That asymmetry is why serving stacks talk about the two phases separately, why time-to-first-token and tokens-per-second have different bottlenecks, and why people bother to run prefill and decode on separate machines. Prefill is a compute problem. Decode is a memory problem. And the thing decode is bottlenecked on is precisely the cache.

What it buys

Strip the cache out and the cost of generation is easy to write down. To produce token $t$ you must rebuild the $k,v$ of all $t$ preceding tokens, so over $N$ generated tokens you pay

\[1 + 2 + \cdots + N \;=\; \frac{N(N+1)}{2} \quad\text{token-passes, instead of}\quad N\]

Drag $N$ and watch the top bar bend.

这种不对称,就是推理框架要把两个阶段分开谈的原因,也是首 token 延迟和 tokens/s 卡在不同瓶颈上的原因,更是有人愿意把 prefill 和 decode 拆到不同机器上跑的原因。Prefill 是个算力问题,decode 是个内存问题。 而 decode 卡住的那个东西,恰恰就是缓存。

它买到了什么

把缓存拿掉,生成的代价很好写。要产出第 $t$ 个 token,你必须把前面 $t$ 个 token 的 $k,v$ 全部重造一遍,所以生成 $N$ 个 token 一共要付

\[1 + 2 + \cdots + N \;=\; \frac{N(N+1)}{2} \quad\text{次 token 前向,而不是}\quad N \text{ 次}\]

拖动 $N$,看上面那条怎么弯上去。

What it costs

Nothing is free. The cache is a list, and a list grows:

\[\text{bytes} \;=\; 2 \times L \times H_{kv} \times d_{\text{head}} \times N \times \text{sizeof(dtype)}\]

The 2 is K and V. $L$ is layers, $H_{kv}$ is key/value heads, $N$ is context length. Everything except $N$ is fixed by the architecture, so the cache is exactly linear in how much you’ve said.

Put real numbers in: 32 layers, 8 KV heads, head dim 128, fp16 — a shape typical of an 8B-class open model — comes out at 128 KB per token. A 32K conversation is 4 GB. Of cache. For one user.

它的代价

没有免费的东西。缓存是一张清单,而清单会长:

\[\text{字节数} \;=\; 2 \times L \times H_{kv} \times d_{\text{head}} \times N \times \text{sizeof(dtype)}\]

那个 2 是 K 和 V。$L$ 是层数,$H_{kv}$ 是 key/value 头数,$N$ 是上下文长度。除了 $N$ 以外全被架构定死了,所以缓存的大小和你说了多少话严格成正比

代进真实数字:32 层、8 个 KV 头、头维度 128、fp16——一个 8B 级开源模型的典型形状——算下来是每个 token 128 KB。一段 32K 的对话就是 4 GB。光是缓存。而且只是一个用户。

Two consequences, and the second is the one people underrate.

It eats your batch size. Concurrency is what makes serving economical, and every concurrent user needs their own cache. The memory left after the weights divides by 4 GB, and that quotient is how many conversations your GPU can hold. Grouped-query attention (GQA) exists mostly to shrink that number’s denominator — fewer KV heads, proportionally smaller cache. It works, and it’s a constant factor applied to a term that still grows linearly.

It eats your bandwidth. Look again at the last stat in the panel: the bytes re-read per generated token. Decode has to stream the whole cache through the memory bus for every single token it emits. Your tokens-per-second is set by that number far more than by your GPU’s FLOPs. Long context doesn’t just cost storage — it makes every subsequent token slower.

Where this goes

So the KV cache is not a hack. It’s the direct consequence of causal masking, and it converts generation from quadratic to linear. It is also, structurally, a lossless list that grows without bound, and that is the thing everything after this post is trying to get rid of.

The next move is not to make the list smaller. It’s to notice that the list is only ever consumed through one operation — $\mathrm{softmax}(q K^{\top})V$ — and to ask whether that operation could be rearranged so the list never has to be stored at all. Take the softmax out and it can: the whole history collapses into a single fixed-size matrix. That’s the next post.


One table, one row per token, and every arrow pointing forward.

References

  • Attention / the transformer — Vaswani et al., Attention Is All You Need (arXiv:1706.03762)
  • Where the cache is first spelled out for inference — Shazeer, Fast Transformer Decoding: One Write-Head is All You Need (arXiv:1911.02150) — also the origin of multi-query attention
  • GQA — Ainslie et al., GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints (arXiv:2305.13245)
  • Paging the cache instead of over-allocating it — Kwon et al., Efficient Memory Management for LLM Serving with PagedAttention (vLLM, arXiv:2309.06180)
  • Why decode is memory-bound — Pope et al., Efficiently Scaling Transformer Inference (arXiv:2211.05102)

两个后果,而第二个通常被低估。

它吃掉你的 batch size。 并发才让推理服务在经济上成立,而每个并发用户都要一份自己的缓存。权重之外剩下的显存除以 4 GB,那个商就是你这张卡能同时装下多少段对话。分组查询注意力(GQA)存在的主要理由,就是把这个分母做小——KV 头少了,缓存等比例缩小。它有效,但它只是给一个仍然线性增长的项,乘上了一个常数因子。

它吃掉你的带宽。 再看一眼面板里最后那个数字:每生成一个 token 要重读的字节数。Decode 每吐一个 token,都得把整个缓存从内存总线上过一遍。你的 tokens/s 被这个数字决定的程度,远大于被 GPU 的 FLOPs 决定的程度。长上下文不只是占地方——它让之后的每一个 token 都变慢。

这条线通向哪

所以 KV 缓存不是什么奇技淫巧。它是因果掩码的直接推论,并且把生成从平方变成了线性。但它在结构上也是一张无损的、无上界增长的清单,而这篇之后的所有内容,都是在想办法摆脱它。

下一步不是把清单做小。而是注意到:这张清单被消费的方式自始至终只有一种——$\mathrm{softmax}(q K^{\top})V$——然后去问,这个运算能不能重排一下,让清单根本不用存。把 softmax 拿掉,它就能:整段历史会坍缩成一个固定大小的矩阵。那是下一篇。


一张表,每个 token 一行,而所有箭头都指向前方。

参考

  • 注意力 / transformer — Vaswani 等, Attention Is All You NeedarXiv:1706.03762
  • 推理侧最早把缓存讲清楚的地方 — Shazeer, Fast Transformer Decoding: One Write-Head is All You NeedarXiv:1911.02150)——也是 multi-query attention 的出处
  • GQA — Ainslie 等, GQA: Training Generalized Multi-Query Transformer Models from Multi-Head CheckpointsarXiv:2305.13245
  • 与其超额分配,不如给缓存分页 — Kwon 等, Efficient Memory Management for LLM Serving with PagedAttention(vLLM,arXiv:2309.06180
  • 为什么 decode 是带宽受限的 — Pope 等, Efficiently Scaling Transformer InferencearXiv:2211.05102