GPU系统拆解-12-LLM 推理的 GPU 主线:Prefill、Decode、KV Cache 与系统约束
GPU系统拆解-12-LLM 推理的 GPU 主线:Prefill、Decode、KV Cache 与系统约束
本文是「GPU系统拆解」系列第 12 篇。
系列导读:GPU系统拆解-00-导读:从架构认知到推理系统的学习路线
上一篇:GPU系统拆解-11-高频 Kernel 设计:从自然并行到资源权衡
下一篇:GPU系统拆解-13-多 GPU 与通信:并行策略、拓扑与扩展代价
这一篇不再泛讲 GPU,而是专门回答一个更接近岗位的问题:为什么 LLM 推理最后会变成一个 GPU 资源管理和系统调度问题。学完这一篇之后,你应该能把
prefill、decode、KV cache、continuous batching、PagedAttention、TTFT / ITL / throughput串成一条完整主线。
1. 先给结论
- LLM 推理从 GPU 视角看,核心不是“不断算下一个 token”,而是两个不同阶段:
prefill和decode。 prefill更像大批量 dense compute,更容易接近compute-bound。decode每步新增计算很少,但要反复读取历史KV cache,更容易变成memory-bound和调度问题。KV cache不只是一个张量,而是推理系统里的核心资源对象,它会直接决定显存占用、并发上限和调度复杂度。continuous batching的本质不是“攒更多请求”,而是把单请求并行度不足转化成多请求并行度叠加。PagedAttention的本质不是新的 attention 数学,而是更灵活的 KV 内存组织方式,用来减少碎片、提高利用率并支持动态请求混跑。- 推理系统不能只看
token/s,至少还要一起看TTFT、ITL、吞吐和显存利用率。
2. 先建立总图:LLM 推理到底在做什么
表面上看,大模型推理只是:
- 输入一段 prompt
- 模型输出下一个 token
- 把新 token 拼回去继续生成
但从 GPU 和系统视角看,它其实天然分成两个阶段:
prefilldecode
这就是理解整套推理系统的第一把钥匙。
如果这一步没分清,后面很多现象都会混在一起:
- 为什么有时算力像瓶颈,有时带宽像瓶颈
- 为什么同一套优化对 prefill 和 decode 效果完全不同
- 为什么服务系统会开始讨论 KV cache、batching 和请求调度
3. Prefill 和 Decode 的本质区别
3.1 Prefill 是什么
prefill 就是把用户给的一整段 prompt 一次性过完整个模型,并为后续生成准备好每层的 K/V。
它的典型特征是:
- 一次处理很多 token
- 形成较大的矩阵计算
- 并行度高
- 更容易把 Tensor Core 吃满
所以从 GPU 角度看,prefill 更像:
大矩阵、大并行度、相对更像 GPU 喜欢的 dense compute。
3.2 Decode 是什么
decode 是 prefill 完成之后,模型逐 token 生成新输出的阶段。
这时每一步通常只新增一个或少量 token,但要读取前面所有历史上下文对应的 K/V。
它的典型特征是:
- 单步新增计算量小
- 需要频繁读取历史 KV
- 小矩阵很多
- kernel 更碎
- 更容易受带宽、缓存和 launch overhead 影响
所以 decode 常常不是“算不动”,而是:
数据喂不过来,或者系统没法把 GPU 持续喂饱。
4. 为什么 decode 往往更难优化
这是 LLM 推理里最关键的系统直觉之一。
很多人会想:
一次只生成一个 token,计算量更小,不应该更容易优化吗?
问题在于,GPU 喜欢的是:
- 规则的大矩阵
- 高并行
- 高算术强度
而 decode 经常更像:
- 单步计算粒度小
- 反复读取历史 KV
- batch 不一定大
- 请求长度不整齐
- kernel 更容易碎片化
所以 decode 经常表现出一种很典型的现象:
- GPU 看起来没有完全吃满
- 但系统就是很难再快多少
这说明瓶颈已经从纯算力转移到了:
- 内存带宽
- cache 行为
- 显存访问模式
- 调度和 batching 策略
5. KV Cache 到底是什么
5.1 为什么要有 KV cache
在 self-attention 里,每层都会生成:
QKV
如果每生成一个新 token,都重新计算历史所有 token 的 K/V,代价会极高。
所以系统会把历史 token 在每层对应的 K/V 存下来,后续直接复用。这就是 KV cache。
5.2 它保存了什么
如果模型有:
L层H个头head_dim = D- 当前序列长度是
T
那么每层都要保存类似:
K: [T, H, D]V: [T, H, D]
整模型就会保存所有层的历史 K/V。
5.3 为什么它是显存大户
很多人一开始以为推理显存主要就是模型权重。
这只对一半。
在真实服务里:
- 权重是静态占用
- KV cache 是随上下文长度和并发动态增长的占用
所以在线推理系统很多时候不是先被算力打爆,而是先被:
- 显存容量
- KV cache 占用
- KV cache 管理策略
打爆。
6. 为什么 KV cache 会变成系统设计问题
如果只是单机 demo,KV cache 看起来只是个 tensor。
但在真实 serving 系统里,它会立刻引出一串系统问题:
- 怎么分配
- 怎么复用
- 怎么回收
- 怎么减少碎片
- 怎么支持不同长度请求混跑
- 怎么让 attention kernel 读取更高效
所以 KV cache 不只是“模型里的缓存”,而是:
推理系统里的核心资源对象。
这也是为什么很多推理系统优化,本质上是在做 KV 资源管理。
7. 为什么 decode 阶段的 attention 越来越像“数据搬运问题”
数学上,attention 可以写成:
1 | softmax(QK^T / sqrt(d))V |
但在 decode 阶段,从系统视角看,它更像:
- Q 只对应当前新 token
- K/V 来自历史缓存
- 需要跨层、跨头、大量读取
- 请求长度和 batch 结构还在动态变化
这意味着 attention 在 decode 阶段很容易变成:
大量必须高效完成的 KV 读取,加上相对有限的新增计算。
所以它越来越像 I/O 问题,而不只是 FLOPs 问题。
这也是为什么 FlashAttention 给人的最大启发,不是新公式,而是:
attention 优化首先是内存系统和数据流优化。
8. 为什么 continuous batching 会成为推理系统核心设计
8.1 传统静态 batch 有什么问题
如果沿用传统静态 batch 思路:
- 收集一批请求
- 一起做推理
- 整批结束后再换下一批
在 LLM 场景里很容易出问题,因为请求之间的:
- prompt 长度不同
- 输出长度不同
- 完成时间不同
这会导致两个典型问题:
- 短请求被长请求拖住
- batch 内活跃请求数会随着请求陆续完成而下降
8.2 Continuous batching 的本质是什么
continuous batching 的核心不是“多攒一点请求”,而是:
在生成过程中持续把新请求插入,把已完成请求移出,让 batch 始终保持流动。
所以它要解决的不是 batch 本身,而是:
- 连续进入
- 连续退出
- 连续调度
- 连续复用资源
8.3 它为什么特别适合 GPU
因为 decode 单请求并行度通常不足。只有把很多请求在时间上重叠起来,才能把 GPU 更持续地喂饱。
所以 continuous batching 的本质是在做这件事:
用系统调度把单请求并行度不足,转化成多请求并行度叠加。
这就是它为什么会成为现代推理服务的核心设计之一。
9. 为什么会出现 PagedAttention
9.1 问题背景:KV cache 很容易碎片化
如果每个请求都连续分配一大块 KV cache,会很快遇到这些问题:
- 请求长度不可预测
- 有的很短,有的很长
- 有的提前结束,有的持续很久
- 回收后留下很多洞
这和操作系统里的动态内存分配问题很像:表面上还有空闲空间,但物理上很碎,不好继续用。
9.2 PagedAttention 的核心思想
PagedAttention 借鉴的是分页思想:
不要求一个请求的 KV 在物理上完全连续,而是把 KV 切成块,再通过映射关系组织起来。
它的核心收益有三个:
- 降低显存碎片
- 提高 KV 分配和回收灵活性
- 支持动态请求混跑
所以它不是在改 attention 的数学本体,而是在改:
attention 访问 KV 的物理组织方式。
9.3 为什么它特别适合在线推理系统
因为在线服务里的请求行为天然是动态的:
- 到达时间随机
- prompt 长度不同
- 生成长度不可知
- 完成时间不同
如果还坚持“每请求一整块连续大缓存”的思路,很快会被碎片和分配效率拖垮。
10. 为什么 prefill / decode 分离越来越重要
10.1 两个阶段喜欢的资源完全不同
前面已经有结论:
- prefill 更偏 compute-bound
- decode 更偏 memory-bound
这意味着它们喜欢的资源模式不同。
prefill 更喜欢:
- 大矩阵
- Tensor Core 吞吐
- 大批量 dense compute
decode 更喜欢:
- 高带宽
- 低延迟调度
- 更高效的 KV 读取
- 更稳定的 batch 和 cache 管理
10.2 这为什么在大厂里有意义
如果把 prefill 和 decode 混在同一个资源池里,常见问题是:
- 大 prompt 请求压制 decode
- 首 token 延迟和单 token 延迟互相干扰
- GPU 在两种不同资源诉求之间来回摆动
所以分离的意义不是炫技,而是为了:
- 更好做 SLA 管理
- 更稳定控制
TTFT和ITL - 让资源类型和负载特征更匹配
11. 为什么推理性能不能只看 token/s
很多人评价推理系统时,只看吞吐量 token/s。这远远不够。
至少还要一起看这几个指标:
11.1 TTFT
TTFT 是 Time to First Token,也就是从请求到收到第一个 token 的时间。
它更容易受这些东西影响:
- prefill
- 排队
- 调度
- 系统流水
11.2 ITL
ITL 是 Inter-Token Latency,也就是相邻两个生成 token 的间隔。
它更能反映 decode 阶段的真实体验。
11.3 Throughput
吞吐量更偏整体系统能力,但高吞吐不等于单请求体验就好。
11.4 为什么这些指标经常互相冲突
因为系统优化往往是 trade-off:
- batch 更大,吞吐更高
- 但
TTFT可能变差 - continuous batching 提高整体利用率
- 但过度追求吞吐,可能让尾延迟变差
所以推理系统不是“把 GPU 利用率拉满”这么简单,而是要在:
- 吞吐
- 延迟
- 显存
- 稳定性
之间做平衡。
12. 用 4090 学这些,有没有价值
有,而且很有价值。
虽然 4090 不是典型数据中心卡,但你在它上面仍然可以学懂这条主线里的大部分核心问题:
- prefill / decode 的差异
- decode 为什么更像 memory problem
- KV cache 为什么会成为核心资源
- continuous batching 为什么有效
- PagedAttention 为什么合理
- attention 为什么会越来越像 I/O 优化问题
你暂时学不到的,主要是这些更数据中心化的部分:
- 多 GPU 大规模并行的真实通信代价
- NVLink / NVSwitch 级别的系统互连
- 超大显存资源池和线上高并发调度细节
但单卡 4090 完全足够学懂方法论。
13. 这一篇必须记住的几句话
- LLM 推理天然分成
prefill和decode,两者瓶颈结构不同。 prefill更像 dense compute,decode更像 memory + scheduling problem。KV cache不只是缓存,而是推理系统里的核心资源对象。continuous batching的本质是把单请求并行度不足转成多请求并行度叠加。PagedAttention的本质是更灵活的 KV 内存组织方式,用来减少碎片并支持动态请求混跑。- 推理系统不能只看
token/s,还要一起看TTFT、ITL和显存利用率。
14. 精简版面试表达
如果面试官问你怎么理解 LLM 推理系统里的 GPU 问题,可以这样答:
我会先把推理分成 prefill 和 decode。Prefill 一次处理整段 prompt,更像大矩阵 dense compute,更容易接近 compute-bound;decode 每步只新增少量计算,但要持续读取历史 KV cache,所以更容易受带宽、缓存和调度影响。也正因为这样,KV cache 会从一个模型内部缓存,变成推理系统里的核心资源对象。为了提高利用率,系统会用 continuous batching 把多请求在时间上叠起来;为了减少 KV 碎片和提高分配灵活性,又会引入像 PagedAttention 这样的分页式组织方式。真正的推理优化不是只盯 FLOPS,而是一起看算力、内存系统、batching、调度和服务指标。



