第 15 课:KV Cache 与推理内存

上一部分我们一直在"造模型"——预训练、对齐、切分、省显存。这一课换个视角:模型训好了,怎么把它服务出去。一次请求在引擎里被切成两段,其中一段几乎不费算力却要吃光显存;这个矛盾决定了对话越长越贵、并发上不去、以及为什么"分页"和"调度"这两个词会成为推理引擎的核心。讲清 KV Cache 的公式与体积、它如何卡住并发、为什么每个 token 都要把模型读一遍,最后看分页 KV 与连续批处理凭什么把吞吐翻十倍。

🎯 学完这 6 步你将能够:
① 说清 prefill 与 decode 两个阶段的差异,并用"重算次数"算出 KV Cache 省下了什么
② 写出 KV Cache 的字节公式,算出 70B 在 8K 上下文下的实际占用
③ 用"显存预算 ÷ 每请求 KV"算出并发上限,并说清 KV 量化与 GQA(即多个查询头分组、共享少数几组 K/V)各能换回多少并发
🗺 本节推理链(5 步逻辑递进 + 1 条汇总支链:从"两个阶段"到"分页与调度")
1模型上线服务,必须同时满足两条硬指标:首 token 延迟要低、每 token 成本要低——这就要求先看清引擎把一次请求切成了哪两段(prefill 与 decode),以及为什么这两段都必须依赖同一个东西:每一层的 cache。KV Cache 是常驻的。
↓
2既然「KV Cache 是常驻的」,所以它的体积会直接写进显存预算——用公式逐项算出来:70B 每个 token 是 0.3125 MiB,8K 上下文就是 2.5 GiB。KV 体积 ∝ 序列长度 × 批大小。
↓
3既然「KV 体积 ∝ 序列长度 × 批大小」,所以一张卡扣掉权重后剩下的空间能装下几个并发请求,就完全由这些 cache 块决定——70B 在 2 张 80 GB 卡上只剩约 8 GB 给 cache,8K context 只够 6 个并发。并发上限由 KV 决定。
↓
4既然「并发上限由 KV 决定」,所以吞吐的唯一出路是让每一步"搬同一份权重"服务更多 token——因为 decode 每一步都要把全部权重从显存读一遍,算术强度只有约 1 FLOP/byte。decode 受带宽限制。
↓
5既然「decode 受带宽限制」要靠加大批量来摊薄,而批量又被前面「并发上限由 KV 决定」这一环卡住,所以必须反过来从公式的因子下手把 KV 做小——少存 GQA/MQA(即多个查询头分组、共享少数几组 K/V)的头数、把 KV 降到 1 字节、或用 MLA(即每层只存一个低秩潜向量)替掉完整的 K/V。把 KV 做小。
↓
6【支链 · 汇总】既然「把 KV 做小」这条路已经被公式定死了下界,因此剩下的空间只能从"怎么分配"里要:分页 cache(PagedAttention,就是把 KV 切成固定块、按需分配)把预留浪费压到 4% 以内、连续批处理让每个 token 迭代都能换人——这就是推理引擎吞吐翻十倍的两个来源,也是第 18 课的地基。
KV CachePrefill & DecodeMemory BandwidthGQA / MQA / MLAPagedAttentionContinuous Batching
1
为什么同一个问题问第二遍会快得多?
提示:把一次请求拆成"读题"和"答题"两段——哪一段能把 GPU 喂饱?以及,如果每一步都从头重算,代价值得吗?
▾
知识背景
把一次生成切成两段的说法,来自 NVIDIA 的推理优化文档:prefill 阶段并行处理整段 prompt(矩阵×矩阵,能把 GPU 算力打满),decode 阶段自回归地一个一个吐 token(矩阵×向量,算力利用率很低)。NVIDIA 在这个口径里明确写了:decode 是 memory-bound。而"必须把历史 K/V 缓存下来"这件事,是 vLLM(即一个通用的高吞吐推理引擎)论文(Kwon 等,Efficient Memory Management for Large Language Model Serving with PagedAttention,SOSP 2023)的出发点——它指出 LLM serving 的性能瓶颈不在算力而在显存,而 vLLM 官方博客给出一句常被引用的读数:LLaMA-13B 单条序列的 KV Cache 可达 1.7 GB(论文正文举的例子其实是 OPT-13B,每 token 约 800 KB)。Orca(Yu 等,OSDI 2022)则先行一步,把调度单位从"整条请求"改成了"一次解码迭代"。前缀缓存(prefix caching)——也就是"相同开头问第二遍会快得多"的那个机制——同样被 vLLM 视为分页 KV 的直接红利。

上一部分我们把模型造了出来;从这一课开始,我们把它服务出去。先把两个阶段说白:prefill(预填充)是把整段 prompt 一次性喂进去,为每个输入 token 算出它在这一层的 K/V;decode(解码)是拿着最后一个 token 一路算下去,一次只产出 1 个新 token,并把它自己的 K/V 追加进缓存。

为什么要缓存?因为自回归生成要求第 t 个 token 看到前面所有 token 的 K/V。如果不缓存,每生成一个新 token 都要把前面所有 token 重新算一遍——可以类比成"每写一个新字,就把整篇文章从头誊写一遍"。缓存下来之后,每个新 token 只需算自己那一份,之前算过的直接取用。

至于"同一个开头问第二遍会快得多":引擎把已算过的前缀对应的 KV 块留在显存里,第二个请求命中同一批块,prefill 几乎不用重算——这就是前缀缓存。

🧮 第一笔账:缓存到底省下了多少次前向
Step 1:不缓存时,生成 n 个 token 要算多少次
要生成第几个 token必须完整前向的 token 数原因
第 1 个n(整段 prompt)每个输入 token 都要算 K/V
第 2 个n + 1前面 n 个要重算,加上刚生成的 1 个
第 t 个n + t − 1靠前的 token 被反复重算
生成 m 个合计m·n + m(m−1)/2随生成长度呈平方增长
取 n = 1000、m = 1000:合计前向 1000×1000 + 1000×999/2 = 1,499,500 次 token 级前向。注意这个数里绝大多数是"重算旧 token 的 K/V"——纯浪费。
Step 2:有缓存时,同一个任务要算多少次
方案每个新 token 要算的 K/V合计(n = m = 1000)相对
不缓存n + t − 1 个 token1,499,5001.0×(基准)
KV Cache只算自己那 1 个 token2,0001/750
算术:有缓存时合计 = n(prefill)+ m(decode 每个新 token 各 1 次)= 1000 + 1000 = 2,000;1,499,500 ÷ 2,000 = 749.75 ≈ 750 倍。这就是"不缓存会 O(n²)、缓存后是 O(n)"这句话的具体含义。代价只有一个:这些 K/V 必须一直留在显存里。
Step 3:两段的算术强度差了三个数量级
阶段输入形状算术强度(FLOP/byte)瓶颈
prefill整段 prompt 一起算(矩阵×矩阵)约 1,000算力(compute-bound)
decode一次 1 个 token(矩阵×向量)约 1显存带宽(memory-bound)
算术强度 = 每搬运 1 字节数据能换多少次浮点运算。两段相差约 1,000 倍,这解释了为什么同一个引擎里,prefill 需要优化算力(分块、FlashAttention(即把注意力分块计算、减少显存读写)),而 decode 需要优化搬运量(把 KV 做小、把批量做大)——Card 4 会把 decode 这笔账算到底。
🎯 关键认知

KV Cache 不是"一个优化技巧",而是自回归解码能成立的前提:它把生成 n 个 token 的 K/V 计算量从平方级压回线性级(本例 750 倍)。但它换来的代价是一块随序列长度与并发数常驻的显存——这块显存有多大、怎么分配、怎么共享,就是接下来 5 张卡片的全部内容。

AI 助教
👋 对 prefill / decode 两段有疑问?比如「为什么 decode 算力利用率这么低」「前缀缓存到底缓存了什么」?

我会用具体数字帮你算。
2
一个 8K 上下文的 70B 请求,KV Cache 要占多少显存?
提示:先数"每个 token 每一层要存几个向量、每个向量多长",再乘上层数与 token 数。注意 GQA 下只存 KV 头,不存 Q 头。
▾
知识背景
KV Cache 的字节公式在各家引擎文档与教材里写法一致:2 × L × Hkv × dh × s × b——开头的 2 是"Key 和 Value 各一份",L 是层数,Hkv 是 KV 头数(不是 Q 头数),dh 是每头维度,s 是序列长度,b 是每个元素的字节数(bf16 = 2)。GQA(Grouped-Query Attention)由 Ainslie 等提出(arXiv:2305.11147,2023),把多个 Q 头分组共享一组 K/V 头;更早的 MQA(Multi-Query Attention)由 Shazeer 提出(arXiv:1911.02150,2019),让所有 Q 头共享唯一一组 K/V。NVIDIA 的推理优化文档把 Llama 2 70B 列为"用 GQA 的典型例子":它的 64 个 Q 头压到 8 个 KV 头——这是 70B 的 KV 能用得起的关键。公式里没有 Q 头数,这正是 GQA/MQA 对 serving 的全部意义。

上一张卡只说了"KV 要常驻",没说多大。这一张把它算准——因为后面所有关于并发、吞吐、成本的判断,都建立在这个数上。

先看公式里每一项能不能动:层数 L 由模型结构定死;KV 头数 Hkv 是 GQA 的产物(70B 是 8);每头维度 dh 通常 128;序列长度 s 由用户决定;字节数 b 由精度决定(bf16 是 2、fp8 是 1)。所以一句话:KV Cache ∝ 序列长度 × 批量 × 层数 × KV 头数 × 每头维度 × 精度,而与参数量无关。

这个"与参数量无关"很反直觉,可以类比成「会议纪要」:会议开得越久(s)、参会人越多(b),纪要就越厚;而纪要的厚度跟"这家公司有多少员工"(参数量)毫无关系。

🧮 第二笔账:一个 token 值多少字节
Step 1:把公式逐项列出来
符号含义Llama 2 70B 取值能不能动
2Key 一份 + Value 一份2不能
L层数80不能
HkvKV 头数(非 Q 头数)8(GQA,Q 头是 64)能(Card 5)
dh每头维度128不能
s序列长度(prompt + 已生成)4096 / 8192 / …由用户决定
b每个元素字节数bf16 = 2能(Card 5)
公式:Bytes = 2 × L × Hkv × dh × s × b。注意 s 是整个序列(prompt 加已生成),不是一个请求只算一次——它每一步都在长。
Step 2:算出 70B 的"每 token 成本"
模型2 × L × Hkv × dh × b每 token每 token(MiB)
Llama 2 70B(GQA,8 头)2 × 80 × 8 × 128 × 2 B327,680 B0.3125 MiB
同名模型若用 MHA(64 头)2 × 80 × 64 × 128 × 2 B2,621,440 B2.5 MiB
LLaMA-13B(MHA,40 层 40 头)2 × 40 × 40 × 128 × 2 B819,200 B0.78125 MiB
算术:2×80×8×128×2 = 327,680 B = 320 KiB = 0.3125 MiB。对照第 2 行:如果 70B 不用 GQA,每 token 就是 2.5 MiB——差 8 倍(64 ÷ 8 = 8)。第 3 行用来做交叉验证:13B 每 token 0.78 MiB,0.78125 MiB × 2048 = 1600 MiB = 1.5625 GiB ≈ 1.68 GB,与 vLLM 博客给出的"LLaMA-13B 单序列最多 1.7 GB"同量级 ✓。
Step 3:乘上长度与批量,看它怎么长大
序列长度 sb = 1(单请求)b = 4(4 个并发)b = 16
2,0480.625 GiB2.5 GiB10 GiB
4,0961.25 GiB5 GiB20 GiB
8,1922.5 GiB10 GiB40 GiB
32,76810 GiB40 GiB160 GiB
算术:2.5 GiB = 0.3125 MiB × 8192 ÷ 1024 ✓(0.3125 × 8192 = 2560 MiB = 2.5 GiB)。注意 s 与 b 是相乘关系:一个 8K 请求 2.5 GiB,16 个并发就是 40 GiB——比 70B 的 bf16 权重(140 GB)确实小,但它是可变量,而权重是常量。长上下文 + 高并发时,KV 会反超权重:32K × 16 并发 = 160 GiB(折算 160 × 1.0737 ≈ 171.8 GB),已经超过权重 140 GB。
🎯 关键认知

KV Cache 的大小只由"序列长度 × 批量 × 结构 × 精度"决定,与模型参数量无关。这一条推翻了直觉:大家以为"模型越大越费显存",但在 serving 场景里,真正随业务量增长的是 KV——权重是买机器时定下的常数,KV 是每个用户、每轮对话都在长的变量。所以能装多少并发,不取决于模型多大,而取决于 KV 多小(下一张卡把这句话算成钱)。

AI 助教
👋 对 KV 体积公式有疑问?比如「为什么公式里没有 Q 头数」「FP8 能把 KV 砍一半吗」?

报个模型和序列长度,我帮你把账算出来。
3
显存里权重占大头,为什么并发数还是上不去?
提示:把一张卡的显存分成"权重"和"每个活跃请求各一份的 KV"两块,再算扣掉权重之后还剩多少给 KV。
▾
知识背景
vLLM 论文(Kwon 等,SOSP 2023,arXiv:2309.06180)给出一组直观的实测:在当时的 serving 系统里,只有 20.4% ~ 38.2% 的 KV 显存份额真正存着 token 状态,其余全被"预留但没用"和碎片吃掉(该读数亦被 Kwon 的博士论文《vLLM: An Efficient Inference Engine for Large Language Models》,UC Berkeley 2025 引用)。换句话说:不是显存不够,而是显存没被用上——但即便把这部分全捡回来,能装多少并发仍由 KV 的绝对体积决定。vLLM 论文(SOSP 2023)给的另一条基线也值得记住:它相对当时最优的 FasterTransformer 提升 2–4× 吞吐,而这些提升全部来自"同样的显存能装下更多请求",模型本身一点没改。

上一张卡算出了"每个请求要多少 KV"。这一张把它放进一张卡的真实预算里——因为并发数是除出来的,不是调出来的。

先把预算列清楚。一张 80 GB 卡上跑 70B,要同时放三样东西:权重、这一批请求的 KV、以及框架自身的开销(激活、临时缓冲、通信缓冲、显存碎片)。第一项是常数,第三项通常是几个 GiB 的固定开销,于是能装几个请求,就完全由第二项也就是 KV 决定——可以类比成一个固定面积的车库:先要腾出位置停那辆"货车"(权重),剩下的空间能停几辆"私家车"(并发请求),取决于每辆车占多大(上下文长度)。

🧮 第三笔账:一张卡到底能装几个请求
Step 1:先看单卡的显存预算表
项70B 在一张 80 GB 卡上说明
权重(bf16)140 GB单卡放不下(呼应第 14 课)
权重(bf16,TP = 2)70 GB / 卡张量并行把权重切一半
框架开销(激活 / 缓冲 / 碎片)约 2 ~ 5 GB经验区间(估算,随实现与序列长度浮动;本卡按保守下界 2 GB 估)
留给 KV 的余量约 8 GB80 − 70 − 约 2 = 约 8 GB
算术:80 − 70 = 10 GB 是上限;扣掉估算的框架开销后,实际可用约 8 GB。这 8 GB 就是全部可用于 KV 的空间——它要装下所有并发请求的 KV Cache。关键细节:张量并行不只切权重,也切 KV——注意力是按头分给各张卡的,所以每张卡只存"自己那几个头"的 K/V(70B 有 8 个 KV 头,TP = 2 时每卡 4 个)。
Step 2:除以"每卡每请求 KV",得到并发上限
上下文长度每卡每请求 KV(TP = 2 → 4 个 KV 头)8 GB 能装几个说明
2,0480.3125 GiB25 个8 ÷ 0.3125 = 25.6
4,0960.625 GiB12 个8 ÷ 0.625 = 12.8
8,1921.25 GiB6 个8 ÷ 1.25 = 6.4
32,7685 GiB1 个8 ÷ 5 = 1.6
算术:每卡每 token = 2 × 80 × (8 ÷ 2) × 128 × 2 B = 163,840 B = 0.15625 MiB;8K 上下文即 0.15625 × 8192 = 1,280 MiB = 1.25 GiB ✓(正好是全模型口径 2.5 GiB 的一半)。这一行行的除法就是"为什么并发上不去"的全部答案:上下文翻一倍,并发砍一半。32K 上下文时两张 H100 只能服务一个请求——GPU 算力利用率会掉到个位数,而你已付了两张卡的租金。
Step 3:四个能换回并发的杠杆
杠杆做法每卡权重每卡每请求 KV(8K)可装并发
基准TP = 2,GQA,bf16 KV70 GB1.25 GiB6
① KV 量化到 fp8KV 的 b 从 2 降到 1(权重不变)70 GB0.625 GiB12
② 提高张量并行到 TP = 8每卡只留 1 个 KV 头(权重也随之切到 1/8)17.5 GB0.3125 GiB约 194
③ 前缀缓存命中共享相同 system prompt 的块70 GB按命中比例减免最多再翻数倍
④ 换 MLA 结构每层只存一个潜向量由基座决定再降(见 Card 5)再升
算术:① KV 量化只改 KV 的字节、不改权重,所以仍在「TP = 2、70 GB/卡、约 8 GB 余量」的口径下:1.25 ÷ 2 = 0.625 GiB → 12 并发 ✓。② 提高 TP 要连权重一起重算:TP 从 2 到 8,每卡权重 140 ÷ 8 = 17.5 GB,KV 余量升到 80 − 17.5 − 约 2 ≈ 60 GB(不再沿用基准的 8 GB),同时每卡每请求 KV 再 ÷4 → 0.3125 GiB,于是并发 = 60 ÷ 0.3125 ≈ 194——② 的提升来自「每卡 KV 变小」与「每卡权重变小、余量变大」两者叠加。注意边界:TP 最多只能切到 KV 头数(70B 是 8),再往上就无法靠切头继续摊薄 KV 了——主流实现(如 TensorRT-LLM)会把 KV 复制到每张卡,此时每卡 KV 不再下降。优先级通常是:前缀缓存(免费)→ KV 量化(近乎免费)→ 提高 TP(要付通信代价,见第 13 课)→ 换结构(要重训)。
🎯 关键认知

并发上限 = (单卡显存 − 权重 − 框架开销)÷ 每请求 KV。这个式子里,只有分母是你业务上真正在消耗的东西。所以一个反直觉的结论是:推理成本的主变量不是模型参数量,而是"上下文长度 × 并发数"——同一个 70B,做 2K 上下文的客服和做 32K 上下文的文档分析,单卡能服务的并发差 25 倍(25 个 vs 1 个)。

AI 助教
👋 对并发上限有疑问?比如「为什么不是按请求数限流」「TP 越大并发越多吗」?

给我你的卡型和上下文长度,我把并发算出来。
4
为什么每生成一个 token,都要把整个模型重新读一遍?
提示:算一下这一步做了多少次浮点运算、又搬了多少字节数据,然后比一比这两个数。
▾
知识背景
NVIDIA 的推理优化文档把 decode 直接定性为 memory-bound:每一步只处理 1 个 token,却要把全部权重从显存搬到计算单元。vLLM 论文的出发点也是同一句话——LLM serving 的瓶颈是显存。判断依据叫算术强度(arithmetic intensity,roofline 模型的核心量):每搬运 1 字节换多少次浮点运算。业内给出的量级是:prefill 约 1,000 FLOP/byte、batch size = 1 的 decode 约 1 FLOP/byte。硬件侧的三个口径(与第 13、14 课保持一致):H100 SXM 的 HBM3 带宽 3.35 TB/s、BF16(即一种低精度浮点格式,硬件吞吐更高)稠密峰值约 989 TFLOPs(官方标称的 1,979 是"带稀疏"口径)。把这两个数相除,就得到这张卡的"算力/带宽拐点"。

上一张卡说明了"并发被 KV 卡住"。但还有一半没解释:为什么单请求的每个 token 也这么慢、这么贵?答案在"搬运"这两个字上。

先看 decode 一步到底做了什么:它把当前 token 的向量,依次乘过每一层的权重矩阵。每一层的权重都必须从显存里读出来——不是读一次就够,而是每生成一个 token 都要重读一遍全部权重。GPU 的算力单元很快,但显存带宽是有限的;当"要搬的数据"远多于"要算的活",耗时就被搬运时间决定。这就是 memory-bound 的含义——可以类比成"每写一个字都要把整座图书馆的书重新搬出来翻一遍":书本身没变,但你每次都付出了搬运的代价。

🧮 第四笔账:decode 一步能被搬多少字节卡住
Step 1:一步的耗时下限 = 要搬的字节 ÷ 带宽
量取值说明
要搬的权重70 GB / 卡(TP = 2)bf16,每步都要重读
H100 HBM3 带宽3.35 TB/s单卡
一步耗时下限约 20.9 ms70e9 ÷ 3.35e12
单请求吞吐上限约 48 token/s1 ÷ 0.0209
算术:70e9 B ÷ 3.35e12 B/s = 0.0209 s,即 20.9 ms/token,上限 1 ÷ 0.0209 ≈ 47.8 token/s。这个推算与业界「单请求、数十 token/s」的实测口径同量级 ✓——说明瓶颈确实是搬权重,不是算不动。(提醒:70B 的 bf16 权重 140 GB 单卡放不下,所以这里始终是 TP = 2 的每卡视角。)。
Step 2:算力其实远远用不上
量单请求(b = 1)怎么来的
这一步的浮点运算2 × 70e9 = 1.4e11 FLOPs每参数 2 次乘加
这一步搬运的字节约 1.4e11 B权重本体
算术强度约 1 FLOP/byte1.4e11 ÷ 1.4e11
H100 的"算力/带宽拐点"约 295 FLOP/byte989e12 ÷ 3.35e12
差距约 295 倍295 ÷ 1
算术:989e12 ÷ 3.35e12 = 295.2 FLOP/byte。低于这个拐点就是带宽受限。单请求只有 1 FLOP/byte,差 295 倍——也就是说这张卡的算力有 99.7% 的时间在等数据。这就是"decode 慢"的定量解释。口径提醒:Step 1 取「TP = 2 的每卡视角」(70 GB),本步取「全模型视角」(70e9 参数 → FLOPs 2×70e9 = 1.4e11、权重 1.4e11 B);分子分母同比缩放,两种视角算出的算术强度都约 1 FLOP/byte,结论不变。
Step 3:批量是唯一的解药(因为权重可以共用)
批量 b算术强度相对拐点结果
11 FLOP/byte差 295 倍严重带宽受限
88 FLOP/byte差 36.9 倍仍带宽受限
3232 FLOP/byte差 9.2 倍仍带宽受限,但吞吐已接近 32 倍
约 295约 295 FLOP/byte≈ 拐点才开始转向算力受限
算术:批量 b 时,FLOPs 变成 2·N·b,而搬运的字节几乎不变(还是那一份权重),所以算术强度 ≈ b FLOP/byte。关键洞察:批量把"同一份权重的搬运"摊给了更多 token——b = 32 时,一次 20.9 ms 的搬运服务 32 个 token,理论吞吐约 32 ÷ 0.0209 ≈ 1,530 token/s,而每个 token 的延迟几乎不变(批量 1 → 32 时每 token 延迟增幅有限,本卡按经验取 < 30%——经验值,需按机型实测)。要到 b ≈ 295 才转成算力受限——在此之前,"加并发"几乎是免费的。
🎯 关键认知

decode 每一步的耗时由"搬运多少权重"决定,而不是由"上下文多长"决定——这解释了两件事:① 为什么对话变长时,每 token 的耗时基本平稳(只有 KV 那一小部分在涨),总时间大致线性;② 为什么批量几乎免费——因为所有请求共用同一份权重,搬运一次就能服务一批。所以推理引擎的核心工作只有一句话:让每一步都在"有足够多 token 可用"的状态下跑——这正是下一张卡(把 KV 做小)与第 6 张卡(把调度做好)的分工。

AI 助教
👋 对"带宽受限"有疑问?比如「为什么量化能加速 decode」「MoE(即把大模型拆成多个小专家、每次只激活其中几个)为什么天生适合 decode」?

我会带你算每一步搬了多少字节。
5
KV Cache 是必需的,那能不能少存一点?
提示:回到公式,逐个因子问一遍"这个数能不能变小、代价是什么"。注意有些因子由模型结构定死、有些由精度决定。
▾
知识背景
把 KV 做小有三条公开的路线。① MQA(Shazeer,arXiv:1911.02150,2019):所有 Q 头共享唯一一组 K/V;② GQA(Ainslie 等,arXiv:2305.11147,2023):折中方案,把 Q 头分组、每组共享一组 K/V——NVIDIA 的推理文档指出,原本用 MHA 训好的模型可以用一小部分原始训练算力"uptrain"成 GQA,质量接近 MHA 而效率接近 MQA(这是 GQA 论文给出的迁移路线);而 Llama 2 70B 是在预训练时就原生采用 GQA,并非从 MHA uptrain 而来;③ MLA(Multi-head Latent Attention):不再逐头存 K/V,而是每层每个 token 只缓存一个低秩潜向量,K/V 在前向时再还原出来——这条路线由 DeepSeek-V2 提出。第四条是KV 量化:把 KV 的元素从 bf16(2 B)降到 fp8/int8(1 B)甚至更低,代表工作是 KIVI(arXiv:2402.02750)。这四条路线共同的代价是:都会或多或少动到质量。

上一张卡片说明了 decode 为什么受带宽限制;这一张把问题倒过来问:既然 KV 的公式这么清楚,能不能直接把它变小?

回到公式 2 × L × Hkv × dh × s × b,逐个因子审一遍:L 由模型定死、s 由用户定、dh 基本固定、2 是定义。真正能动的是两个:Hkv(少存几个头) 和 b(每个数用更少字节);MLA 走的是第三条路——不再存 K/V 本身,改存一个更窄的替代品。

这个取舍可以类比成存照片:与其每张都保留 RAW 原图(bf16、全部 KV 头),不如存压缩图(fp8、更少的头)——省得多,但要接受细节上的损失。

三条路线省的量级差得很远,先记住三个数:MQA 把所有 Q 头压到一组 K/V,相对 MHA(每个 Q 头各配一组 K/V)省 64 倍;GQA(70B 的 8 个 KV 头)省 8 倍;而 MLA 换的是另一条路——不存完整的 K/V,每层每个 token 只缓存一个低秩潜向量。DeepSeek-V2 论文报告:相对其前代 DeepSeek 67B,MLA 把 KV Cache 压掉 93.3%(降到约 1/15);由论文 Table 1 的公式 (d_c+d_h^R)l ≈ 4.5·d_h·l 可推得一个更可比的读数——MLA 的每 token 缓存只相当于 2.25 组 GQA(此换算为本课按该公式推导,非论文原话),对照 70B 的 8 组 GQA,就是再小约 3.6 倍(8 ÷ 2.25 ≈ 3.6)。这就是为什么「换结构」虽然要重训,却是把 KV 砍得最狠的一刀。

🧮 第五笔账:三个杠杆各能省多少
Step 1:公式里的五个因子,谁动得了
因子能不能动手段代价
2(K 与 V)不能——
L(层数)不能删层会毁掉模型—
Hkv(KV 头数)能MQA / GQA(要重训或 uptrain)质量略降,需验证
dh(每头维度)基本不能——
s(序列长度)业务决定滑窗 / 驱逐 / 摘要压缩丢信息
b(每元素字节)能KV 量化到 fp8 / int8长程任务上更敏感
注意 s 这个因子要特别小心:它最大、最诱人,但也最不能乱动——滑窗与驱逐都是有损的,会改变模型实际"看到"的内容。工程上更常见的做法是"对 s 做管理"(前缀缓存、上下文压缩)而不是"对 s 做截断"。
Step 2:Hkv 这一刀——MHA 与 GQA 的 8 倍差
方案KV 头数每 token8K 上下文相对
MHA(64 个 Q 头各配一组 K/V)642.5 MiB20 GiB1.0×
GQA(Llama 2 70B 实际采用)80.3125 MiB2.5 GiB1/8
MQA(所有 Q 头共享一组)10.0390625 MiB0.31 GiB1/64
算术:2×80×64×128×2 = 2,621,440 B = 2.5 MiB;换成 8 个头得 0.3125 MiB;比值 64 ÷ 8 = 8 ✓(同理 MQA 再差 8 倍,合计 64 倍)。这一刀是结构级的、也是最省事的一刀——因为它不引入任何运行时开销,只要模型本身这么训。
Step 3:b 这一刀——精度换一半
KV 精度每元素字节每 token8K 上下文相对 bf16
fp3240.625 MiB5 GiB2×
bf16(基准)20.3125 MiB2.5 GiB1.0×
fp8 / int810.15625 MiB1.25 GiB1/2
算术:0.3125 ÷ 2 = 0.15625 MiB,8K 下 2.5 → 1.25 GiB ✓。回到 Card 3 的账:这把并发从 6 个翻到 12 个。而且这一刀不需要重训模型,是部署期就能做的选择——这也是为什么"KV 量化"在线上几乎成了默认选项。
🎯 关键认知

三个杠杆的性价比排序很清楚:KV 量化(部署期可做、省一半、无需重训)> GQA(结构级、省 8 倍、需训模型时决定)> MLA(省得更多但要新架构)。而它们共同的代价提醒我们一件事:KV Cache 不是纯工程问题,它同时是"质量 vs 成本"的交换点——压得越狠,长程推理越容易掉分,所以任何压缩都必须配回归评测(这是第 19 课的主题)。

AI 助教
👋 对 KV 压缩有疑问?比如「KV 量化会不会掉点」「MLA 到底存了什么」?

把结构或精度报给我,我帮你算省多少。
6
显存明明空着,引擎为什么还说"塞不下"?
提示:想想"按最长可能长度先预留"会发生什么——预留了却没用完的那部分,算不算浪费?再想想调度单位是"整条请求"还是"一步"。
▾
知识背景
这一张讲两个把吞吐拉起来十倍的机制,它们都有明确的一手出处。① PagedAttention(vLLM,Kwon 等,SOSP 2023):论文与官方博客给出两个关键读数——当时的系统因"预留 + 碎片"平均浪费 60% ~ 80% 的 KV 显存;改用分页块后,浪费只发生在每个序列的最后一个块,实测浪费低于 4%。同一篇还指出,块表让"不同序列共享同一批物理块"成为可能(配 Copy-on-Write),并行采样这类场景的显存可再降最多 55%、吞吐最多再提 2.2×。② 连续批处理(continuous batching):Orca(Yu 等,OSDI 2022)把调度单位从"整条请求"改为"一次解码迭代",在 GPT-3 175B 上达到 36.9× 于 NVIDIA FasterTransformer 的吞吐(同为 190 ms 延迟目标下:0.185 → 6.81 请求/秒)。后续 Sarathi-Serve(OSDI 2024)用 chunked prefill(把长 prefill 切成小块、混进正在跑的 decode)解决"新请求的 prefill 打断正在进行的 decode"造成的卡顿(generation stall)。也要记住反向证据:vAttention(Microsoft,ASPLOS 2025)指出分页内核本身有开销——分页版 prefill 内核比非分页版慢最多 37%、decode 慢最多 12%,块大小选错会让内核耗时差 约 1.9×。

上一张卡片从公式的因子上把 KV 压小了;这一张处理一个更"冤枉"的浪费:显存并没有真的用满,但引擎说没地方了。

原因是"预留"。为了不让 KV 边生成边重新分配,早期系统会按模型支持的最大长度给每个请求预留一块连续显存——比如给每个请求先占 8K 的位置。真实对话平均只用到一两千 token,剩下的就白占着。更麻烦的是碎片:每块大小不一,释放后留下的空隙往往拼不出下一个请求要的形状。于是出现"总空闲很多、但就是放不下"——这可以类比成停车场按"最大车型"给每辆车划线:车位总量明明够,但短车占着长车位、剩下的零碎空隙又停不下下一辆。

🧮 第六笔账:浪费在哪、怎么捡回来
Step 1:预留式分配浪费了多少
项数值说明
按最大长度预留8,192 token每个请求都先占满
实际平均使用约 1,024 token短对话居多的典型分布
利用率12.5%1024 ÷ 8192
浪费87.5%1 − 12.5%
业界论文口径60% ~ 80%vLLM 论文实测(含碎片)
算术:1024 ÷ 8192 = 12.5%,浪费 87.5%。这个"理想化算例"比论文给的 60~80% 更极端(因为论文的实测里输出更长),但方向一致:预留制的浪费是第一位的。
Step 2:分页把浪费压到最后一块
方案分配单位浪费来源浪费率
预留式连续分配整条请求(按最大长度)预留未用 + 内部碎片 + 外部碎片60% ~ 80%
分页(PagedAttention)固定块(如 16 token)只有每个序列的最后一块< 4%
块大小取 16 时的一块16 token最坏情况浪费 15 token—
分页的关键是"逻辑连续、物理不连续":每个序列有一张块表,把逻辑块号映射到任意空闲的物理块。这样块可以按需分配,浪费只可能发生在最后没填满的那一块。代价写在反向证据里:分页内核本身有开销(prefill 最多慢 37%、decode 最多慢 12%),所以"省下的显存"要能换来"足够的并发"才划算。
Step 3:连续批处理让每个迭代都能换人
调度单位一条请求结束后GPU 在等谁实测
整条请求(早期)整个批次一起结束才能换等最慢的那条基准
一次迭代(Orca 起)任何时刻都能插入新请求、移出已结束的不等任何人36.9×(GPT-3 175B)
+ chunked prefill(Sarathi-Serve)把长 prefill 切块混进 decode不再被打断容量再 2.6× ~ 5.6×
算术:同一延迟目标(190 ms)下,吞吐从 0.185 请求/秒 提到 6.81 请求/秒,6.81 ÷ 0.185 ≈ 36.8(论文口径记作 36.9×)✓。原因是"队头阻塞"被消掉了——调度单位从"最慢的请求"变成了"一次前向",每一步都可以重新组批。注意顺序:连续批处理需要"动态分配 KV"才成立,而这正是分页提供的——两者是配套的,不是可替换的两个技巧。
🎯 关键认知

"吞吐翻十倍"不是一个技巧做出来的,而是三件事叠起来:分页(把浪费从 60~80% 压到 <4%)→ 连续批处理(让每一步都能重新组批)→ 分块 prefill(让长请求不再打断短请求)。它们共同的立足点是这一课反复出现的那句话:KV 才是推理显存的主角。而反向证据同样重要——分页内核本身要付出 12% ~ 37% 的单步开销,所以"分页一定更快"是错的,正确的说法是"分页让更多请求同时在跑,因此整体吞吐更高"。这套调度机制的内部细节(调度策略、抢占、前缀缓存淘汰)是第 18 课"推理引擎"的主题。

AI 助教
👋 对分页与调度有疑问?比如「块大小怎么选」「前缀缓存什么时候失效」?

我会用具体数字和出处回答。