/ AI Infra
[Infra-4] Prefill-Decode Disaggregation
A practical walkthrough of prefill-decode disaggregation: why prefill and decode conflict, what is actually disaggregated, how it affects TTFT and TPOT, and why KV cache transfer becomes the new systems bottleneck.
我一开始理解 PD 分离时,很容易把它想成一个很朴素的拆分:把 prefill 放到一批 GPU 上,把 decode 放到另一批 GPU 上。
这个说法没有错,但它没有说出为什么这件事在大模型推理里会变得重要。真正关键的是:Prefill 和 Decode 虽然都属于一次 LLM 推理,但它们对 GPU、显存、网络和调度器的要求几乎是两种 workload。把它们混在同一组 worker 里,系统会同时追求“大 batch 高吞吐”和“小步低延迟”,最后两边都容易被对方干扰。
所以我现在更愿意这样理解 PD 分离:
PD 分离不是模型结构优化,而是 LLM serving 的系统架构优化。
它把资源需求不同的两个推理阶段拆开,让 Prefill 和 Decode 可以分别调度、分别扩容、分别优化。它真正改变的不是 Transformer 怎么算,而是推理服务怎么组织计算、缓存和数据流。
1. 先把一次推理拆成两个阶段#
一次自回归 LLM 推理通常可以分成两个阶段:
用户输入 prompt
↓
Prefill:一次性处理输入,生成初始 KV Cache
↓
Decode:逐 token 生成输出,并持续读取与追加 KV Cache如果用户输入 16k tokens,模型输出 1k tokens,那么系统不是用同一种方式处理这 17k tokens。前面的 16k 输入会集中进入 prefill;后面的 1k 输出会进入逐步 decode。
这个分界非常重要,因为它直接决定了性能指标该怎么拆。
Prefill 主要影响 TTFT:Time To First Token
Decode 主要影响 TPOT:Time Per Output TokenTTFT 决定用户等多久看到第一个 token。TPOT 决定回答吐出来时是否顺滑。
2. Prefill 更像大矩阵计算#
Prefill 是把完整 prompt 一次性送进模型。假设输入长度是 16k tokens,模型需要对这 16k tokens 做完整 forward,并为后续生成准备 KV Cache。
它的特点更接近:
计算密集
矩阵乘法规模大
并行度高
适合更大的 batch
主要压力在算力和大块连续计算所以 Prefill 很像一个“大 batch、大 GEMM、高吞吐”的任务。prompt 越长,这个阶段越重。长上下文场景里,TTFT 往往首先被 prefill 成本拉高。
这也是为什么很多推理系统会单独优化 chunked prefill、prefix caching、prompt batching、长上下文调度。它们表面上是在处理 prompt,实质上是在控制首 token 之前那一大段计算和缓存生成。
3. Decode 更像高频访存任务#
Decode 则完全不同。模型已经拿到了 prompt 的 KV Cache,接下来每次只生成一个 token 或一小步 token:
step 1: 生成 token_1
step 2: 生成 token_2
step 3: 生成 token_3
...每一步的计算量不大,但每一步都要读取历史 KV Cache。上下文越长、并发 sequence 越多,decode 对显存带宽、KV Cache 访问和调度延迟越敏感。
它的特点更接近:
逐 token 生成
单步计算量小
频繁读取 KV Cache
memory bandwidth 压力大
对延迟抖动非常敏感所以 Decode 不是一个“单次算很久”的任务,而是一个“小步、频繁、延迟敏感”的任务。它最怕中间被重活打断。一次 decode token 原本 20 ms,如果中间被一个长 prompt 的 prefill 插进来,用户看到的就可能是回答突然卡顿。
4. 不做 PD 分离时,矛盾会发生在哪里#
如果不做 PD 分离,常见结构是每个 worker 同时负责 prefill 和 decode:
Worker 1: Prefill + Decode
Worker 2: Prefill + Decode
Worker 3: Prefill + Decode
Worker 4: Prefill + Decode这看起来简单,但问题在于两类任务喜欢的执行形态不同:
Prefill 喜欢大 batch、高吞吐、连续计算。
Decode 喜欢低延迟、低抖动、小步快跑。两者放在同一组 GPU worker 里,会互相影响。
4.1 Prefill 会阻塞 Decode#
假设一个 worker 正在服务多个 decode 请求,每个请求都在稳定地产生 token。这时来了一个 32k prompt 请求。如果系统把这个长 prompt 的 prefill 插入同一个 worker,GPU 会花一段时间处理大输入。
结果可能变成:
原本:
token 1: 20 ms
token 2: 21 ms
token 3: 19 ms
插入长 prefill 后:
token 1: 20 ms
token 2: 180 ms
token 3: 24 ms
token 4: 160 ms平均吞吐可能还行,但用户体感会变差,因为 decode 不再稳定。在线服务里,P99 ITL、TPOT 抖动和长尾延迟经常比平均值更重要。
4.2 Decode 也会打碎 Prefill#
反过来,如果一个 worker 上已经有很多活跃 decode sequence,每一步都要抢 GPU 资源,那么新的 prefill 请求也很难形成规整的大 batch。
这会导致:
Prefill batch 被打碎
大矩阵计算不够饱满
GPU 算力利用率下降
TTFT 上升也就是说,混跑不是单向伤害。Prefill 会让 decode 抖,decode 也会让 prefill 不够高效。
5. PD 分离到底分离了什么#
PD 分离做的事情,就是把这两个阶段拆到不同资源池:
用户请求
↓
Router
↓
Prefill Pool
- Worker P1
- Worker P2
- Worker P3
↓ 生成 / 查找 KV Cache
Decode Pool
- Worker D1
- Worker D2
- Worker D3
- Worker D4
↓
逐 token 输出更具体地说:
Prefill worker 负责处理输入 prompt,生成或复用 KV Cache。
Decode worker 负责消费 KV Cache,持续生成输出 token。这时两个阶段之间真正流动的核心数据不再是普通请求对象,而是:
KV Cache所以 PD 分离的关键问题会立刻变成:
Prefill 生成的 KV Cache,如何高效传给 Decode?这也是 Mooncake、LMCache、vLLM disaggregated prefill、SGLang HiCache 这类系统共同关注的问题。PD 分离表面上是在拆计算阶段,深一层看,其实是在把 LLM serving 从 compute-centric 推向 KVCache-centric。
6. 它为什么能提高资源利用率#
Prefill 和 Decode 对硬件的压力不同:
| 阶段 | 更主要的瓶颈 | workload 特征 |
|---|---|---|
| Prefill | 算力 | 大矩阵乘法、计算密集、适合 batch |
| Decode | 显存带宽 / KV 读取 / 调度延迟 | 小步迭代、访存密集、延迟敏感 |
如果混在一起,GPU workload 会频繁在两种形态之间切换:一会儿大块 prefill,一会儿细碎 decode。调度器既要照顾新来的 prompt,又要照顾正在生成的 sequence,还要管理 KV block、continuous batching、prefix cache、preemption 等状态。
拆开之后,目标会清晰很多:
Prefill Pool:
追求高吞吐
合并长 prompt batch
控制 TTFT
Decode Pool:
追求低 TPOT
保持 token 生成稳定
管理活跃 sequence 和 KV 读取这不是简单“多一层架构”,而是让每类 worker 的任务更单一。任务单一之后,调度策略、batch 策略、扩缩容策略和性能指标才更容易对齐。
7. 它为什么能分别优化 TTFT 和 TPOT#
LLM serving 里经常同时看两个指标:
TTFT = Time To First Token
TPOT = Time Per Output Token不做 PD 分离时,TTFT 和 TPOT 很容易互相牵连。一个长 prompt prefill 会拉高正在 decode 请求的 TPOT;大量 decode 请求也会让新的 prefill 排队或 batch 不规整。
做 PD 分离之后,系统可以把优化目标拆开:
Prefill 集群优化 TTFT。
Decode 集群优化 TPOT / ITL。这也会改变 benchmark 的设计方式。比如我会分别问:
Prefill only:
16k prompt 下,TTFT <= 1s 时最大并发是多少?
Decode only:
TPOT <= 30ms 时,最多能稳定服务多少活跃 sequence?这比只看一个总 tokens/s 更接近线上服务的真实体验。一个系统可能总吞吐不错,但首 token 很慢;也可能首 token 很快,但输出过程一顿一顿。PD 分离的价值之一,就是让这些问题可以被分开测、分开调。
8. 独立扩缩容才是线上价值#
真实业务里的输入长度和输出长度并不对称。
有些请求是长输入、短输出:
输入 64k tokens
输出 100 tokens这类请求主要压 Prefill。比如长文档问答、代码仓库分析、长上下文 RAG,系统大部分成本花在理解输入和生成 KV Cache 上。
另一些请求是短输入、长输出:
输入 1k tokens
输出 8k tokens这类请求主要压 Decode。比如创作、长报告生成、持续对话,系统成本会在逐 token 生成阶段持续累积。
如果所有 worker 都是 Prefill + Decode 混合角色,就很难按业务形态精确分配 GPU。PD 分离后,资源池可以独立调整:
Prefill 节点数量:按输入长度、TTFT SLO、长 prompt 占比扩容。
Decode 节点数量:按输出长度、TPOT SLO、活跃 sequence 数扩容。这就是资源池化的意义。它让推理服务不再只能粗糙地“多加几张卡”,而是可以问:到底是首 token 慢,还是生成过程慢;到底该扩 prefill,还是该扩 decode。
9. 最大代价:KV Cache 传输#
PD 分离不是免费优化。它最大的新成本就是 KV Cache 传输。
在混合 worker 里,prefill 和 decode 通常在同一个 worker 内连续发生。KV Cache 生成后可以留在本地,decode 直接用。
但在 PD 分离里:
Prefill worker 生成 KV Cache
↓
跨 GPU / 跨 worker / 跨节点传输
↓
Decode worker 消费 KV CacheKV Cache 不是小对象。它的大小大致和下面这些因素成正比:
KV Cache 大小 ∝ 层数 × token 数 × KV head 数 × head dim × batch size长上下文下,KV Cache 很容易进入 GB 级别。如果传输路径不够快,就会出现非常尴尬的情况:
Prefill 已经算完了。
Decode 还在等 KV。
TTFT 反而变高。所以 PD 分离能不能真正带来收益,关键不只看 prefill 和 decode 是否拆开,还要看:
KV 传输速度
网络带宽和拓扑
RDMA / GPUDirect RDMA 能否用上
缓存命中率
KV block 管理
prefill/decode 负载比例
cache-aware scheduling这也是为什么 Mooncake 会强调 KVCache-centric scheduler、Transfer Engine、KVCache Store;LMCache 会把 KV Cache 抽象成可以跨 engine、跨查询复用和移动的缓存层。PD 分离一旦落到工程实现,核心战场很快就从“怎么拆阶段”变成“怎么搬、怎么复用、怎么调度 KV”。
10. PD 分离和 Prefix Caching 的关系#
PD 分离和 prefix caching 不是同一个概念,但两者高度相关。
Prefix caching 解决的是:
相同 prefix 不要重复 prefill。例如很多请求共享:
system prompt
长文档上下文
固定 agent 工具说明
多轮对话历史如果这些 prefix 的 KV Cache 可以复用,系统就不必每次都重新做完整 prefill。
PD 分离解决的是:
Prefill 和 Decode 分别由不同资源池执行。两者结合之后,系统形态会继续变化:
Prefill worker 不仅生成 KV,也查询已有 KV。
Decode worker 不仅消费本地 KV,也可以从远端获取 KV。
Router 不仅按负载调度,也按 KV 命中情况调度。这时 KV Cache 就不只是一个 worker 内部的临时显存状态,而更像一个可以被命中、转移、复用和淘汰的数据资产。
11. 和 TP、PP、DP 有什么区别#
PD 分离很容易和传统分布式推理里的并行方式混在一起。它们都涉及多卡,但分的东西不同。
| 概念 | 分的是什么 | 目的 |
|---|---|---|
| Tensor Parallel | 模型参数矩阵 | 单层计算并行 |
| Pipeline Parallel | 模型层 | 跨层流水 |
| Data Parallel | 请求或模型副本 | 提高服务并发 |
| Expert Parallel | MoE expert | 分布专家计算 |
| PD 分离 | 推理阶段 | 分离 prefill 和 decode 资源 |
所以 PD 分离更像 serving-level disaggregation,而不是模型内部并行。
它也可以和 TP、PP、DP 同时存在。例如:
Prefill Pool 内部使用 TP=4。
Decode Pool 内部也使用 TP=4。
Prefill 和 Decode 之间通过 KV transfer 通信。
外层再用多个副本做服务级扩容。这样看就清楚了:TP/PP/EP 主要回答“一个模型怎么切到多卡上算”,PD 分离主要回答“在线请求的两个阶段应该由哪些资源池服务”。
12. 什么时候收益最大#
PD 分离在下面几类场景里更容易有明显收益。
12.1 长上下文#
例如:
8k / 16k / 32k / 64k / 128k prompt上下文越长,prefill 越重,单独优化 prefill 的价值越高。长文档、长代码、长对话历史、复杂 agent prompt 都属于这类。
12.2 高并发在线服务#
高并发下,请求到达时间、prompt 长度和输出长度都不一致。混跑时,prefill 和 decode 的互相干扰会被放大。PD 分离可以让 decode pool 更稳定,从而改善 TPOT 和 P99 ITL。
12.3 输入输出长度差异大#
如果业务里同时存在长输入短输出、短输入长输出,统一 worker 很难同时照顾两种压力。PD 分离可以让 prefill 和 decode 按真实压力独立扩缩容。
12.4 Prefix cache 命中率高#
如果大量请求共享 system prompt、文档上下文或 agent 模板,那么 KV Cache 复用价值很高。PD 分离和 prefix caching 结合后,调度器可以围绕 KV 命中情况做更精细的路由。
12.5 多节点部署#
单机多卡也能做 PD 分离,但真正的系统价值通常在多节点集群中更明显。因为这时资源池化、跨节点 KV transfer、缓存层和调度器会共同决定服务上限。
13. 什么时候不一定值得做#
PD 分离不是万能方案。下面几类场景收益可能有限,甚至可能变差。
13.1 Prompt 很短#
如果输入只有几十到几百 tokens,prefill 本身不贵。拆开后增加的调度、路由和 KV 传输成本,可能超过 prefill/decode 分离带来的收益。
13.2 KV 传输太慢#
如果网络、拓扑或 KV transfer 实现不够好,就可能出现:
省下的 prefill 干扰 < 新增的 KV 传输成本这时 PD 分离会把瓶颈从 GPU 计算转移到网络和缓存层。
13.3 请求之间几乎没有复用#
如果每个请求的 prompt 都完全不同,prefix cache 命中率很低,那么 KV-centric 架构的收益会下降。当然,PD 分离仍然可能通过降低干扰来改善稳定性,但缓存复用这一块价值就不明显。
13.4 系统规模很小#
单机单卡、小 batch、短 prompt benchmark 里,PD 分离往往只是增加复杂度。它更适合已经出现 prefill/decode 干扰、长尾延迟、资源比例失衡或 KV Cache 复用需求的服务。
14. 一个 4 卡例子#
假设有 4 张 GPU,不做 PD 分离时:
GPU0: Prefill + Decode
GPU1: Prefill + Decode
GPU2: Prefill + Decode
GPU3: Prefill + Decode某一时刻来了一个 32k prompt 请求。这个请求会被放到某个 GPU 上做很重的 prefill。如果这张 GPU 上还有正在 decode 的请求,它们的 token 生成就会被拖慢。
做 PD 分离后,可以变成:
GPU0-GPU1: Prefill Pool
GPU2-GPU3: Decode Pool32k prompt 先去 Prefill Pool 处理。生成 KV Cache 后,再传给 Decode Pool 继续逐 token 输出。
这个例子里,PD 分离的直接效果不是“prefill 本身一定更快”,而是 decode pool 不再直接被长 prefill 打断。它改善的是资源隔离、调度目标和输出稳定性。
15. 我最后怎么记 PD 分离#
如果只记一句话,我会这样记:
PD 分离的本质,是把 LLM 推理从“一个 worker 同时处理输入和输出”,改成“Prefill 专门处理输入,Decode 专门生成输出,中间用 KV Cache 连接”。它的价值主要体现在五件事上:
- Prefill 和 Decode 的资源特征不同,混在一起会互相干扰。
- 拆开后,TTFT 和 TPOT 可以被分别测量、分别优化。
- Prefill Pool 和 Decode Pool 可以按业务压力独立扩缩容。
- KV Cache 可以从 worker 内部状态变成可复用、可调度的数据资产。
- 代价也很明确:KV Cache 传输、缓存管理和调度复杂度会成为新的系统瓶颈。
所以 PD 分离不是“拆开两个函数”这么简单。它代表的是 LLM serving 的一个架构转向:系统不再只围绕 GPU 算力组织请求,而是开始围绕 TTFT、TPOT、KV Cache、缓存命中率、资源池化和 SLO 来组织请求。
真正做工程判断时,我会先问:
现在的问题是 prefill 重,还是 decode 抖?
KV Cache 能不能高效传输和复用?
拆开之后节省的干扰,能不能覆盖新增的 KV transfer 成本?如果这三个问题都有清晰答案,PD 分离才会从一个听起来漂亮的架构词,变成一个真正能改善线上服务的系统手段。
参考资料#
- Mooncake: A KVCache-centric Disaggregated Architecture for LLM Serving: https://arxiv.org/abs/2407.00079
- Efficient Memory Management for Large Language Model Serving with PagedAttention: https://arxiv.org/abs/2309.06180
- LMCache: An Efficient KV Cache Layer for Enterprise-Scale LLM Inference: https://arxiv.org/abs/2510.09665
- vLLM Documentation, Automatic Prefix Caching: https://docs.vllm.ai/en/latest/features/automatic_prefix_caching/
- vLLM Documentation, Disaggregated Prefilling: https://docs.vllm.ai/en/latest/features/disagg_prefill/