06 - PD 分离
本文转载自开源文档站 AI Infra 学习笔记,作者 程治玮(Se7en / cr7258),仅作个人学习存档,内容与配图保持原样。
- 原文链接:https://se7en.mintlify.app/talks/06-disaggregating-prefill-and-decoding
- 源仓库:https://github.com/ai-infra-learning/docs
- 许可证:MIT License
- 原始分享:2025-09-21 · 主讲 程治玮
问题讨论
在大语言模型推理过程中,prefill 阶段和 decode 阶段具有截然不同的计算特性:
- prefill 阶段需要并行处理整个输入序列来生成首个 token,属于计算密集型操作。
- decode 阶段则逐个生成后续 token,需要频繁访问 KV cache,属于内存密集型操作。
传统的 continuous batching 将两个阶段混合处理,导致相互干扰,难以同时满足 TTFT(首 token 延迟)和 TPOT(token 间延迟)的严格要求。为了解决这一问题,PD 分离架构应运而生,通过将 prefill 和 decode 分配到不同的 GPU 实例上,针对各自特性进行专门优化。这种分离式设计不仅消除了阶段间的干扰,还能显著提升系统的有效吞吐量(Goodput),为大规模 LLM 服务提供了更优的解决方案。
1 吞吐量(Throughput)vs 有效吞吐量(Goodput)
目前,大多数 LLM 服务系统(如 vLLM、TensorRT-LLM)都以吞吐量(Throughput) 作为主要性能指标——即单位时间内处理的请求数(RPS)或生成的 token 数。这种度量方式直观,并且与成本($/req)有直接关联,因此被广泛采用。
实际上,下游应用的类型多种多样,它们在用户体验上的延迟需求各异,因此需要满足的服务等级目标(SLO)也存在显著差异。大模型服务中最常用的 SLO 包括:
- TTFT (Time To First Token):首 token 响应延迟,直接影响用户的等待体验。
- TPOT (Time Per Output Token):衡量两个连续生成的 token 之间的平均延迟,决定交互的流畅程度。
例如,实时聊天机器人更关注低 TTFT 以保证响应及时,而 TPOT 只需快于人类阅读速度(约 250 词/分钟)即可;相反,文档摘要则更强调低 TPOT,以便更快地产生完整摘要。

单纯依赖 Throughput 作为指标,并不能反映延迟表现,系统看似处理了大量请求,但其中不少未能满足 SLO,最终呈现给用户的仍是不理想的服务体验。
- Throughput(吞吐量):通常指系统单位时间内处理的 token 数或请求数。很多工作把“提高吞吐量”作为主要优化目标,但在实际场景下,这并不直接代表用户体验。
- Goodput(有效吞吐量):指系统在满足延迟约束(如 TTFT/TPOT SLO)的前提下,真正完成的请求数量。如果一个请求因为延迟过长而被用户放弃,或者超过服务约束而无效,那么即便它产生了 token,也不能算作有效产出。
在 DistServe 论文中,引入了 Goodput 概念,即在满足 SLO(TTFT 和 TPOT 要求)的前提下,每秒完成的有效请求数。与单纯的吞吐量相比,Goodput 是更优的衡量指标,因为它能够体现请求在满足 SLO 情况下的吞吐水平,从而同时反映成本效益与服务质量。
为了简要说明 Goodput,假设某个应用要求至少 90% 的请求满足 TTFT < 200ms 且 TPOT < 50ms,则可以得到如下定义:
Goodput (P90 TTFT < 200ms 且 P90 TPOT < 50ms) 表示在至少 90% 的请求同时满足 TTFT < 200ms 和 TPOT < 50ms 的条件下,系统所能维持的最大每秒请求数。
下图展示了 一个简单的例子:某应用的吞吐量为 10 RPS(每秒请求数),但由于延迟约束的限制,只有 3 RPS 的请求满足 SLO,因此该系统的 Goodput 仅为 3 RPS。可以想象,用户在这样一个 高吞吐但低 Goodput 的系统中,依然会感受到较差的服务体验。

2 Prefill 与 Decode 共置导致干扰
在 LLM 服务中请求的生命周期通常包含两个阶段:prefill(生成首个 token)和 decode(逐步生成后续 token)。大多数现有系统(如 vLLM、TensorRT-LLM)采用 continuous batching 技术,将 prefill 和 decode 混合在一起统一批处理。这种方式确实能够提升整体吞吐量,但由于两者计算特性和 SLO 目标差异显著,将它们共置在同一 GPU 上往往并不理想。

如下图所示,continuous batching 会带来明显的干扰。当 prefill 和 decode 被放在同一批次时,decode 请求的延迟(TPOT)会被显著拉长,而 prefill 请求的首 token 延迟(TTFT)也会有所增加。

图中展示了三种不同的执行方式:
- 1P+nD(棕色柱子):1 个 prefill 与 n 个 decode 混合批处理。
- nD(蓝色柱子):仅包含 decode 请求的批处理。
- prefill-only(红色虚线):仅运行 prefill 请求的延迟。
在 prompt 长度为 128 时,相比仅包含 decode 的请求,延迟增加约 1.8 倍;而当 prompt 长度为 1024 时,干扰效应显著放大,decode 延迟提升至 12.6 倍。

由于这种干扰,如下图所示,当服务必须同时满足 TTFT 和 TPOT 的 SLO 时,系统往往需要进行资源的过度配置才能达到延迟目标,尤其是在任一 SLO 要求较严格的情况下。

3 PD 分离的整体思路
直观的思路很简单:将 prefill 和 decode 分离到不同的 GPU 上,并为每个阶段定制并行策略。这自然解决了前面提到的两个问题:
- 没有干扰:prefill 和 decode 各自独立运行,更快地完成计算,也更容易满足各自的 SLO。
- 资源分配与并行策略解耦:可以针对 prefill 和 decode 分别进行优化。
下图展示了在这样一个分离式系统中,请求是如何被处理的。当一个请求到达系统时,它会先被分配到 prefill worker 完成 prefill 阶段;随后系统将其中间状态(主要是 KV Cache)迁移到 decode worker,并执行多步 decode 以生成后续 token;当生成完成后,请求才会离开系统。

让我们通过一个简单的实验来看看为什么 PD 分离是有益的。我们在一张 A100-80GB GPU 上运行一个 130 亿参数的 LLM,请求到达服从泊松分布,输入长度为 512,输出长度为 64。我们逐步增加请求速率(x 轴),并在下图测量两类延迟(P90 TTFT 和 P90 TPOT,y 轴)的变化。
假设我们设定 SLO:P90 TTFT = 0.4 秒,P90 TPOT = 0.04 秒(下图中的横线)。实验结果表明:在单卡情况下,现有系统大约可以在 3 rps 下满足 TTFT 的延迟约束,而 TPOT 只能维持在 1.6 rps(下图左边)。由于必须同时满足两个约束条件,现有共置系统的 Goodput = min(3, 1.6) = 1.6 rps/GPU。
在分离之后,性能得到了显著提升。如果单独处理一个阶段,prefill worker 和 decode worker 的 rps 都优于之前的结果 —— 如下图右边所示,一个 prefill worker 大约可达到 5.6 rps,一个 decode worker 大约可达到 10 rps。更重要的是,我们现在可以灵活地分配资源,例如配置 2 个 prefill worker + 1 个 decode worker(记作 2P1D),共 3 张 GPU。此时:
Goodput (2P1D) = min(5.6 × 2, 10) = 10 reqs/s ÷ 3 GPUs ≈ 3.3 rps/GPU。
这个实验表明,即便没有引入任何并行优化,仅仅通过简单的分离,Goodput 就提升了约 2 倍。
