用户感受到的"模型速度",其实由两个阶段拼成。Prefill(预填充)并行读入整个prompt,逐层计算并写入KV缓存,最后产出第一个token;Decode(解码)则是自回归循环,一次只算一个token,读权重、读全部历史KV、算出下一个token、再追加新KV。

两个阶段的瓶颈完全不同。理解这一点,是所有推理优化——量化、批处理、投机解码——的基础。

打开网易新闻 查看精彩图片

算术强度决定瓶颈在哪

关键差异在于算术强度,也就是每个字节能做多少次浮点运算。

Prefill阶段整个序列并行,矩阵乘法饱满,瓶颈在算力(FLOPS),优化方向是更强的GPU、更好的kernel。Decode阶段单token、逐次访存,瓶颈是内存带宽,优化方向是更快的显存、更小的权重和KV。

直观理解:prefill是"一次搬完全部货物,路不是问题,装卸能力是问题";decode是"每次只搬一件,但每次都要跑全程,路的通行速度决定一切"。

批处理如何改变瓶颈

单条请求解码时,读一遍权重只为产出1个token,算力大量闲置,纯带宽受限。把多条请求拼成batch后,权重只需读一次,同时为N个请求各算一个token;访存量几乎不变,计算量翻了N倍,瓶颈从带宽滑向算力。

这正是吞吐与时延的取舍所在:批处理提升了总吞吐,但单请求的TPOT通常会变差。服务侧追求吞吐就会加大batch;单机本地推理没有并发,自然回到带宽受限状态。

这也是llama bench中pp128(prefill)和tg64(decode)分开测的原因:两个数字受不同因素支配,必须分别看。

KV缓存:用空间换时间

注意力要求每个新token回看全部历史。若不缓存,每个token的K/V都要重算,代价是平方级。缓存后只算增量,代价是显存占用随上下文线性增长:

KV内存 ≈ 层数 × 上下文长度 × KV头数 × 头维度 × 精度

长对话因此有双重成本:

  • 静态成本:KV缓存占用或预留更多内存;
  • 动态成本:每个新token要attend的历史位置变多,解码变慢、访存变大。

所以"上下文越长回答越慢、越占显存"不是错觉,是结构性的。对应手段包括:开新会话、摘要压缩旧轮次、调小上下文上限、KV量化。

三个指标对应三个维度

TTFT(首token时间)对应prefill性能,决定"点了发送要等多久才有反应";TPOT(每token时间)对应decode性能,决定"流式输出看起来流不流畅"。

端到端时延 ≈ TTFT + 输出token数 × TPOT。这个分解式是全篇最有实用价值的公式,任何"总时长"问题都能拆到这两个可独立优化的项上。

聚合吞吐则是所有在途请求的总tokens/s,是服务端的核心指标,靠批处理提升。

一份排查清单

觉得"响应慢",先测TTFT,问题在prefill:缩短prompt、换算力更强或优化更好的后端。

觉得"输出慢",问题在decode:用更小或更低精度的模型(量化)、更高端宽的硬件、避免慢速互联上的CPU/GPU卸载、控制活跃上下文长度。

显存不够,优先算KV公式,压缩上下文或量化KV。服务要扛量,加批处理,接受单请求时延的少量上升。

一句话总结:prefill拼算力,decode拼带宽,KV缓存决定显存与长上下文代价;TTFT、TPOT、吞吐三个指标分别对应这三个维度,所有优化技巧都是在移动这三条边界。