GPU系统拆解-12-LLM 推理的 GPU 主线:Prefill、Decode、KV Cache 与系统约束

本文是「GPU系统拆解」系列第 12 篇。
系列导读:GPU系统拆解-00-导读:从架构认知到推理系统的学习路线
上一篇:GPU系统拆解-11-高频 Kernel 设计:从自然并行到资源权衡
下一篇:GPU系统拆解-13-多 GPU 与通信:并行策略、拓扑与扩展代价

这一篇不再泛讲 GPU,而是专门回答一个更接近岗位的问题:为什么 LLM 推理最后会变成一个 GPU 资源管理和系统调度问题。学完这一篇之后,你应该能把 prefilldecodeKV cachecontinuous batchingPagedAttentionTTFT / ITL / throughput 串成一条完整主线。

1. 先给结论

  • LLM 推理从 GPU 视角看,核心不是“不断算下一个 token”,而是两个不同阶段:prefilldecode
  • prefill 更像大批量 dense compute,更容易接近 compute-bound
  • decode 每步新增计算很少,但要反复读取历史 KV cache,更容易变成 memory-bound 和调度问题。
  • KV cache 不只是一个张量,而是推理系统里的核心资源对象,它会直接决定显存占用、并发上限和调度复杂度。
  • continuous batching 的本质不是“攒更多请求”,而是把单请求并行度不足转化成多请求并行度叠加。
  • PagedAttention 的本质不是新的 attention 数学,而是更灵活的 KV 内存组织方式,用来减少碎片、提高利用率并支持动态请求混跑。
  • 推理系统不能只看 token/s,至少还要一起看 TTFTITL、吞吐和显存利用率。

2. 先建立总图:LLM 推理到底在做什么

表面上看,大模型推理只是:

  • 输入一段 prompt
  • 模型输出下一个 token
  • 把新 token 拼回去继续生成

但从 GPU 和系统视角看,它其实天然分成两个阶段:

  1. prefill
  2. decode

这就是理解整套推理系统的第一把钥匙。

如果这一步没分清,后面很多现象都会混在一起:

  • 为什么有时算力像瓶颈,有时带宽像瓶颈
  • 为什么同一套优化对 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 里,每层都会生成:

  • Q
  • K
  • V

如果每生成一个新 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 切成块,再通过映射关系组织起来。

它的核心收益有三个:

  1. 降低显存碎片
  2. 提高 KV 分配和回收灵活性
  3. 支持动态请求混跑

所以它不是在改 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 管理
  • 更稳定控制 TTFTITL
  • 让资源类型和负载特征更匹配

11. 为什么推理性能不能只看 token/s

很多人评价推理系统时,只看吞吐量 token/s。这远远不够。

至少还要一起看这几个指标:

11.1 TTFT

TTFTTime to First Token,也就是从请求到收到第一个 token 的时间。

它更容易受这些东西影响:

  • prefill
  • 排队
  • 调度
  • 系统流水

11.2 ITL

ITLInter-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 推理天然分成 prefilldecode,两者瓶颈结构不同。
  • prefill 更像 dense compute,decode 更像 memory + scheduling problem。
  • KV cache 不只是缓存,而是推理系统里的核心资源对象。
  • continuous batching 的本质是把单请求并行度不足转成多请求并行度叠加。
  • PagedAttention 的本质是更灵活的 KV 内存组织方式,用来减少碎片并支持动态请求混跑。
  • 推理系统不能只看 token/s,还要一起看 TTFTITL 和显存利用率。

14. 精简版面试表达

如果面试官问你怎么理解 LLM 推理系统里的 GPU 问题,可以这样答:

我会先把推理分成 prefill 和 decode。Prefill 一次处理整段 prompt,更像大矩阵 dense compute,更容易接近 compute-bound;decode 每步只新增少量计算,但要持续读取历史 KV cache,所以更容易受带宽、缓存和调度影响。也正因为这样,KV cache 会从一个模型内部缓存,变成推理系统里的核心资源对象。为了提高利用率,系统会用 continuous batching 把多请求在时间上叠起来;为了减少 KV 碎片和提高分配灵活性,又会引入像 PagedAttention 这样的分页式组织方式。真正的推理优化不是只盯 FLOPS,而是一起看算力、内存系统、batching、调度和服务指标。


系列导航