Day 17 · 推理性能核心瓶颈
KV Cache:为什么大模型推理最缺的不是算力而是显存?
很多读者以为:大模型跑得慢,是因为 GPU 算力(FLOPS)不够用。但在真实部署中,大模型在逐 token 生成(Decode)时,GPU 的算力往往大量闲置、利用率低得可怜!真正的死穴是显存带宽(Memory Bandwidth)与显存容量被疯狂吃爆。今天拆解大模型推理的命根子——KV Cache(键值缓存)。
学完你能回答
为什么自回归推理必须把历史 KV 存起来?
学完你能回答
Prefill 与 Decode 两个阶段有什么本质不同?
学完你能回答
为什么说推理是典型的 Memory-bound(访存受限)?
1起点:这些你早就会了
KV Cache 的逻辑,本质就是日常办公里的「草稿纸暂存」:
| 你已经会的 | AI 世界里对应的东西 |
| 写长篇小说:每写一个新段落,不需要把前面的内容全部重抄一遍 | KV Cache 机制:把历史 token 的 K 和 V 存在显存里,每次只算最新 1 个 token 的 Q |
| 读题与作答:先花 5 秒通读上千 token 的题目,再用 1 秒写出一个答案 | Prefill vs Decode:Prefill(预填充,并行算完全文)与 Decode(逐 token 解码) |
| 办公桌堆积:每个人桌上都堆着厚厚的历史档案,桌子不够放了 | KV 显存膨胀:序列长度 × 并发用户数,显存占用比模型权重还大 |
| 卡车运货:发动机马力极大(算力强),但路口太窄每次只能过一辆(带宽小) | Memory-bound(内存墙):算力在摸鱼,时间全花在从 HBM 搬运 KV 数据上 |
| 查资料时把常用页折角,下次直接翻到那一页 | 缓存复用:算过的 K/V 留在显存里随取随用,不用重算 |
| 旺季快递仓库爆仓:货太多堆不下,只能排队等仓位 | 高并发挤爆显存:并发一多 KV Cache 装不下,请求就得排队等显存 |
2为什么需要 KV Cache?(空间换时间)
回忆 Day 9 的公式:生成第 t 个 token 时,我们需要当前 token 的 q_t 去和历史所有位置的 k_1, k_2, …, k_t 做点积,并加权混合 v_1, v_2, …, v_t。
两种推理策略对比
•
没有 KV Cache(傻子模式):
为了吐出第 100 个 token,把前 99 个 token 从头完整过一遍模型,重新算一遍它们的 K 和 V;吐出第 101 个 token 时,再把前 100 个 token 从头算一遍……
生成 N 个 token 的总计算量是 1 + 2 + … + N,即
O(N²),越往后越慢得令人发指!
•
有了 KV Cache(聪明模式):
前 99 个 token 算好的 K 和 V
直接缓存在 GPU 显存里。生成第 100 个 token 时,模型只需要输入这 1 个 token,算出它的 q₁₀₀、k₁₀₀、v₁₀₀;
把 k₁₀₀、v₁₀₀ 追加写入缓存,拿 q₁₀₀ 和缓存里所有 K 做一次点积即可!每步需要「新算」的只有这 1 个 token——从「重算全部历史」变成「读一遍缓存」。
严格来说(类比的边界):Decode 每步除了要读 KV Cache,还要把整套模型权重从显存搬进计算核心——上下文短时,权重搬运才是大头;KV Cache 的搬运量随历史变长线性增长,长上下文下才反超成为主角。
图 17.1:KV Cache 推理流向。空间换时间,避免了历史序列的无休止重复前向计算。
3两段截然不同的人生:Prefill 与 Decode
你每次在聊天窗口发送一段长文本并等待回答时,模型内部会经历两个性格完全相反的阶段:
- 阶段一:Prefill(预填充 / 首 token 生成)——用户输入了一条 2000 token 的 Prompt(约合两三千个汉字——按 1 汉字 ≈ 0.7–1 token 粗算;这是全文唯一一次用「字」,此后统一用 token)。模型把这 2000 个 token 一次性并行扔进 GPU 算力核心,一次性填满 KV Cache 并吐出第一个 token。这个阶段是大矩阵乘法,能把 GPU 算力基本打满,属于计算密集型(Compute-bound)。
- 阶段二:Decode(逐 token 解码)——从第 2 个 token 开始,模型每次只输入 1 个 token(batch 大小为 1)。此时计算量极小(向量乘矩阵),但为了算这 1 个 token,必须把整整 2000 个历史 KV 向量全量从显存(HBM)搬运到芯片计算核心(SRAM)里!耗时全在搬运数据上,算力核心都在干等,属于严重的访存密集型(Memory-bound)。
4显存杀手现形:长序列下的显存海啸
为什么长上下文和大并发这么贵?我们来算一笔震撼的显存账:
KV Cache 显存占用公式
KV Cache 显存 = 2 × 层数(L) × 隐藏维度(d) × 序列长度(S) × 并发批次(B) × 精度字节
以一个常见 70B 模型(80层,8192维,FP16=2字节)为例:
• 处理 1 个 128K 长度的请求,仅 KV Cache 就要霸占 320 GB 显存(示意,按 MHA 假设估算:80 层 × 8192 维 × 128K × 2 字节 × 2(K 和 V)——相当于 4 张 A100 80G 显卡,光存上下文就满了,连模型权重都放不下!);
• 如果要并发服务 100 个用户,显存需求直接奔着几万 GB 去了!
这就是为什么近十年所有架构师都在绞尽脑汁压缩 KV Cache(催生了 MQA、GQA 和 MLA,下节揭秘)!
小结:KV Cache 用显存换时间,消除了历史 K/V 的重复计算,却带来两张新账单——显存容量随「层数 × 维度 × 长度 × 并发」线性膨胀;Decode 每步还要把整块缓存从 HBM 搬进计算核心。怎么把这块缓存压小?明天 Day 18 的 MQA/GQA/MLA 就是干这个的。
5面试视角
面试视角 · 高频考点
面试官可能会问:「大模型推理时的 Prefill 和 Decode 阶段有什么区别?为什么说 Decode 阶段是 Memory-bound(访存受限)的?」
八股答「Prefill 是处理 Prompt,可以并行算,是计算密集型;Decode 是逐 token 生成,每次只算一个 token,受限于显存带宽,是访存密集型。」——背得很熟,若能结合计算访存比(Arithmetic Intensity)解释则更硬核。
本课答「核心是算力与访存的失衡:① Prefill 阶段:输入整个 Prompt 序列 S,进行的是 (S × d) × (d × d) 的 GEMM 矩阵乘法,计算量为 O(S²·d),计算访存比高,能充分打满 GPU Tensor Core;② Decode 阶段:每次输入单个 token,执行的是 (1 × d) × (d × d) 的 GEMV 向量矩阵乘法。每计算一次注意力,必须将显存(HBM)中堆积的所有历史层 KV Cache 全量搬运到芯片 SRAM 缓存中。由于显存读写带宽(如 A100 仅约 2TB/s)远落后于 GPU 浮点算力(312 TFLOPS),算力核心大部分时间处于等待数据传输的饥饿状态,计算瓶颈彻底由算力转向了显存带宽。」
6映射到原文:K3 报告 §5.1
技术报告原文对照 · 《Kimi K3 技术报告》§5.1 KDA 的算法-系统协同设计
「KDA 用固定大小的循环状态 S(维度 d_k × d_v,见 §2.1.1)取代 softmax 注意力中不断增长的键值缓存:其串行更新给并行执行带来挑战,但换来的是一个传输与复用代价都很低的固定大小状态。」
逐词翻译:
- 「不断增长的键值缓存」:就是今天讲的 KV Cache——softmax 注意力每多生成 1 个 token,缓存就多一条 K/V,显存占用和每步搬运量都随长度线性上涨。
- 「固定大小的循环状态」:KDA(线性注意力的一种,Day 19 讲)干脆不存逐 token 的 K/V,只维护一个尺寸固定的状态 S——序列再长,它也不变大。
- 「传输与复用代价都很低」:状态小,搬起来便宜,还能跨请求复用——这正是对今天「Decode 瓶颈在搬运」这条主线的直接回应。
注意 K3 并没有完全抛弃 KV Cache:它的架构是 3 层 KDA 配 1 层 Gated MLA(Day 18/19 讲),MLA 层的缓存还在,只是被压缩过。所以今天学的这笔 KV Cache 账,到 K3 里依然管用。
O(L)
解码每步需从显存读出的历史 KV 量(L=历史长度)
10-20%
未优化解码阶段 GPU 算力利用率(示意)
320 GB
70B 模型单条 128K 序列 KV 显存开销(示意,按 MHA 假设估算)
~2 TB/s
A100 HBM 显存带宽(硬件公开规格)
7自测题(先自己答,再点开看解析)
Q1 什么是 KV Cache?它解决了什么痛点?
解析:KV Cache 在自回归生成过程中,将历史所有 token 的 Key 和 Value 向量保存在显存中。每次生成新 token 时,无需重复计算历史前缀的 K/V——每步要「新算」的从 O(N) 降为 1 个 token 的量(但注意力打分仍需读取全部缓存,是 O(L) 的访存量)。
Q2 为什么 Query(Q)不需要存进 Cache 里?
解析:因为每次生成下一个 token 时,只需要用当前最新 token 的单个 Query 去查询所有历史 Key;历史位置的 Query 在过去的时间步已经用完了,未来再也不会被访问,所以无需缓存。
Q3 为什么大模型生成第 1 个 token(首 token 延迟)通常比后续生成慢?
解析:首 token 生成处于 Prefill 阶段,需要一口气计算并填充整篇长 Prompt 的全部上下文,计算量最大;首 token 吐出后进入 Decode 阶段,每次只处理 1 个 token。
Q4 为什么说 Decode 阶段是 Memory-bound(访存密集型)?
解析:单步计算只有 1 个 token(向量乘矩阵),浮点计算量极小;但为了计算注意力,必须从 GPU 显存里读取全部历史的 KV Cache,时间全消耗在显存搬运带宽上。
Q5 KV Cache 的显存大小与哪些参数成正比?
解析:与层数 L、模型维度 d、序列长度 S、并发批次 B 均呈严格线性正比(显存 ∝ 2·L·d·S·B)。
明天 · Day 18
注意力变体:压缩主线(MQA → GQA → MLA)
大模型显存怎么救?从共享 Key/Value 到低秩投影(MLA),看架构师如何把这块缓存一压再压!
进入 Day 18 →