跳到主要内容

Prefill/Decode 分离原理

核心要点

  • 三条独立观察:prefill compute-bound / decode memory-bound; TTFT / TPOT SLO 正交;并行策略偏好不同
  • 代价:KV cache 必须跨集群转移一次,KV 转移延迟 < TTFT 预算时才划算
  • Chunked prefill 是折中:解决 colocated 干扰但并行策略仍耦合,TTFT 被切碎拉长
  • SLO 正交分解:TTFT 归 prefill 池,TPOT 归 decode 池,Goodput = 二者乘积
  • KV 转移可隐藏:Layer-wise pipelined + zero-copy RDMA + GPU staging,占总延迟可降至 0.1%

本文只讲 PD 分离的原理与调度模型。具体系统实现 (Mooncake / DistServe / SGLang / Dynamo / Splitwise) 在本知识域下各有独立文档展开。

Prefill 跟 Decode 负载特征差在哪

核心问题:计算量 / 访存量 / batch 友好度 / 主导 SLO 各自的差异是什么?

计算量与访存量

设模型隐藏维度为 $d$,层数为 $L$,输入 prompt 长度为 $s$, batch 内有 $B$ 条请求。

Prefill 阶段每条请求的浮点运算量 (不含 attention 的二次项) 近似:

$$\begin{equation} \text{FLOPs}_{\text{prefill}} \approx 2 \cdot s \cdot N_{\text{params}} \label{eq:pd-disagg-prefill-flops} \end{equation}$$

$N_{\text{params}}$ 是模型参数量。

Decode 阶段每生成 1 个 token 的浮点运算量:

$$\begin{equation} \text{FLOPs}_{\text{decode/token}} \approx 2 \cdot N_{\text{params}} \label{eq:pd-disagg-decode-flops} \end{equation}$$

但 decode 每步还需读取整个 KV cache。KV cache 在第 $t$ 步的大小 (FP16):

$$\begin{equation} \text{KVSize}(t) = 2 \cdot L \cdot t \cdot d_{\text{kv}} \cdot \text{sizeof(fp16)} \text{ bytes per request} \label{eq:pd-disagg-kv-size} \end{equation}$$

$d_{\text{kv}}$ 是 KV head 的总维度 (GQA 下显著小于 $d$)。

参考量级:OPT-66B 上 512 个 token 的单条 KV cache 约 1.13 GB (DistServe §2.1[1])。

性能维度对比

维度PrefillDecode
Bottleneck计算 (compute-bound)显存带宽 (memory-bandwidth-bound)
Arithmetic intensity$s$ 线性增长,prompt 长时远超 GPU ridge point$\approx B$, B 不够大就无法饱和算力
Batch 友好度单条长 prompt 已能打满,组 batch 收益小必须靠大 batch 摊销显存读取,对 batch 高度敏感
主导 SLOTTFTTPOT
并行策略偏好TP 较大、PP 看模型规模DP / PP 摊销 KV 容量,TP 主要为带宽
单步耗时$s$ 成正比,可达数百 ms与 KV 长度弱相关,单步约 10–50 ms

@tbl-pd-disagg-phase-compare 负载特征对比

DistServe 的概括是 "a prefill step often takes much longer than a decoding step"[1]; Splitwise 的概括是 prefill compute-intensive、decode memory-intensive[2]; Mooncake 用 "high compute, low memory" vs. "low compute, high memory" 描述[3]

合并部署的问题在哪

核心问题:colocated 干扰是什么?Chunked prefill 能解决多少?

直接 colocated 的相互干扰

把 prefill 与 decode 放在同一 batch 同一组 GPU 上轮转执行,会出现:

  • Prefill 阻塞 decode:一个长 prompt 的 prefill step 单独耗费数百 ms,期间所有正在 decode 的请求都被推迟,P99 TPOT 严重抖动
  • Decode 拖累 prefill:decode 步频繁插入会让 prefill 的大矩阵乘被打断,TTFT 抬升
  • 并行策略只能折中:prefill 偏好的 TP 配比与 decode 偏好的 batch 容量配比互相矛盾

DistServe 把这两条干扰命名为 "prefill-decoding interference",并指出它 "couple the resource allocation and parallelism plans for both phases"[1]

Chunked prefill 的折中与局限

SARATHI 提出的 chunked prefill 方案:把长 prompt 切成固定大小的 chunk (如每 chunk 512 tokens),每个 step 用一个 prefill chunk 填满 batch 剩余位置给 decode 请求 "piggyback"[4]。这种 hybrid batch 让 colocated 部署的单步时长更均匀,pipeline bubble 减少。

但 chunked prefill 仍然在同一组 GPU 上做两件事,无法解决:

  • 并行策略仍然耦合:只能选一套 TP / PP 配比,对 prefill 与 decode 都不是最优
  • TTFT 被 chunk 化拉长:切成 chunk 后每个 chunk 之间夹杂 decode 步,prompt 端到端处理时间增加
  • 硬件型号无法分化:prefill 对算力敏感、decode 对显存带宽敏感,统一机型不能各自最优

SARATHI 报告 chunked prefill 在 LLaMA-13B / A6000 上 decode 吞吐最多提升 10×,端到端 1.33×[4]; DistServe 与 Splitwise 进一步分离硬件后报告比 chunked prefill 类方案更高的 goodput。

PD 分离的调度模型怎么搭

核心问题:物理拓扑 / 单请求生命周期 / 池容量比例怎么配?

物理拓扑

PD 分离把推理集群划分为两个独立池:

职责优化目标典型并行
Prefill 池接收请求、计算 prompt 的 KV cache最小化 TTFT P90/P99TP 较大,batch 较小
Decode 池接收 KV cache、自回归生成 token最大化 token 吞吐同时满足 TPOT SLObatch 较大,TP / PP 看 KV 容量

@tbl-pd-disagg-pool 两池职责

两池之间通过 RDMA / NVLink / RoCE / EFA 网络转发 KV cache (Mooncake / SGLang 等都支持多种 transport)。

单请求生命周期

一条请求的端到端流程:

  1. Router 选址:全局调度器把请求路由到一台 prefill 实例
  2. Prefill 执行:prefill 实例算完整个 prompt,产出 KV cache
  3. Decode 选址:调度器选择一台 decode 实例 (可能基于负载、KV 亲和性、可用显存)
  4. KV 转移:prefill 实例把每层 KV cache 通过 RDMA / NVLink 推送给 decode 实例的显存
  5. Decode 自回归:decode 实例从转移好的 KV cache 开始生成 token,直到 EOS 或达到 max_tokens

SGLang 的实现把 router 称为 Model Gateway, KV transfer 后端可选 Mooncake / NIXL / ASCEND[5]。Mooncake 让 decode 端作为 consumer 主动拉取,prefill 端做 zero-copy RDMA[3]

池容量比例

Prefill 与 decode 的算力需求比依赖工作负载。Mooncake 用 XpYd 记号描述部署比例:X 个 prefill 节点、Y 个 decode 节点。这个比例与下列因素相关:

  • 平均 prompt 长度 $\bar{s}$ 越大,prefill 占比越大
  • 平均输出长度 $\bar{n}$ 越大,decode 占比越大
  • KV cache 越大 (长上下文、未启用 GQA),decode 池显存需求越大

实际选址通常用 binary search + simulation 离线确定 (DistServe 的做法[1]),或在线根据队列长度动态调整。

SLO 怎么正交分解

核心问题:TTFT / TPOT / Goodput 在 PD 分离下怎么各管一池?

PD 分离的关键好处是把端到端 SLO 正交分解

端到端 SLO归属阶段由谁负责主要影响因素
TTFT P90/P99prefillprefill 池容量 + KV 转移延迟prompt 长度、TP 配比、网络带宽
TPOT P90/P99decodedecode 池 batch 大小与显存带宽KV cache 总量、batch 大小、TP / PP
Goodput二者乘积整池规划XpYd 比例、SLO attainment 目标 (如 90%)

@tbl-pd-disagg-slo SLO 拆分

DistServe 把 SLO attainment 定义为"百分比请求同时满足 TTFT 与 TPOT 约束",并报告 P90 attainment 下相比 colocated 系统能服务 7.4× 更多请求、容忍 12.6× 更紧的 SLO[1]

需要注意 KV 转移延迟实质上挤占 TTFT 预算:从用户视角,TTFT 是 prompt 到达到首个 token 返回的全程时间,包含 prefill 计算 + KV 转移 + decode 首步。如果 KV 转移占 TTFT 预算超过一定比例 (典型 < 10%–20%),分离方案的收益就被吃掉了。

KV cache 跨集群传输代价怎么算

核心问题:转移数据量公式 / 转移时间模型 / 怎么隐藏?

转移数据量

单条请求 prompt 长度 $s$、KV cache 大小由公式 $\eqref{eq:pd-disagg-kv-size}$ 给出。一条 13B 模型 ($L=40$, $d_{\text{kv}}\approx 5120$, FP16) 的 prompt 1024 tokens 的 KV cache 约:

$$\begin{equation} 2 \times 40 \times 1024 \times 5120 \times 2 \approx 838 \text{ MB} \label{eq:pd-disagg-kv-example} \end{equation}$$

GQA / MLA 会显著降低 $d_{\text{kv}}$,使 KV 转移量降低一个量级。

转移时间模型

设 prefill 与 decode 之间的有效带宽为 $BW$ (GB/s),单条请求的转移时间:

$$\begin{equation} T_{\text{transfer}} = \frac{\text{KVSize}(s)}{BW} + T_{\text{setup}} \label{eq:pd-disagg-transfer-time} \end{equation}$$

$T_{\text{setup}}$ 是握手、远端内存注册、RDMA WR 提交的固定开销。对于 200 Gbps (25 GB/s) 的 RoCE / IB, 838 MB 转移约 33 ms; NVLink-300GB/s 量级下只需约 3 ms。

转移方式与隐藏

为减少 KV 转移对 TTFT 的拖累,主流做法:

优化机制出处
Layer-wise pipelined transferprefill 算完第 $i$ 层就启动第 $i$ 层 KV 的转移,与后续层计算 overlapDistServe / Mooncake
Zero-copy RDMA双方注册 GPU 显存为 RDMA buffer,避免 CPU 中转Mooncake TransferEngine
异步预拉取decode 端主动 pull, prefill 端无需阻塞等待 ackMooncake
GPU staging buffer当 prefill 与 decode 的 TP 不一致时,先 gather 到连续 buffer 再批量 RDMA,避免 per-token slice 的小包碎片,报告 2–5× 吞吐提升SGLang heterogeneous TP

@tbl-pd-disagg-transfer-opt 转移优化

Heterogeneous TP 的额外开销:当 prefill 池的 TP 与 decode 池的 TP 不相等时 (很常见,因为两者最优 TP 不同),KV cache 在两侧的分片布局不一致,需要额外的 reshape / gather 步骤。SGLang 的 GPU staging buffer 优化正是为此设计。

PD 分离适合什么 / 不适合什么

核心问题:哪些场景值得做 PD 分离?哪些场景反而拖慢?

适用场景

场景原因
长 prompt (chat / RAG / 代码补全)prefill 阶段长,prefill-decode 干扰严重
长输出 (reasoning / agent)decode 阶段长,TPOT SLO 是主要约束
严苛的 SLO 双约束colocated 干扰导致 P99 难达标
异构 GPU 池prefill 用高算力卡 (H100), decode 用大显存 / 低成本卡 (Splitwise 报告 1.4× 吞吐、20% 成本降低[2]
大模型 + 高并发goodput 收益足以摊销 KV 转移开销

不适用场景

场景原因
超短 prompt + 超短输出KV 转移延迟占 TTFT 比例过大
集群间带宽不足$T_{\text{transfer}}$ 接近甚至超过 prefill 时间,分离反而拖慢
极小规模部署 (单节点)没有足够 GPU 划分两池
工作负载高度均匀且短chunked prefill 已能解决 colocated 干扰

@tbl-pd-disagg-applicability 适用与不适用场景

Takeaway

知识点核心结论
三条独立观察prefill compute-bound / decode memory-bound; SLO 正交;并行策略偏好不同
Chunked prefill 局限解决干扰但并行策略仍耦合,TTFT 被切碎拉长
调度模型两池 + Router 选址 + 5 步生命周期 + XpYd 比例配置
SLO 分解TTFT 归 prefill, TPOT 归 decode, Goodput = 二者乘积;KV 转移占 TTFT < 20% 才划算
KV 转移代价13B / 1K prompt ≈ 838 MB; 200 Gbps RoCE ~33 ms; NVLink ~3 ms
隐藏方法Layer-wise pipelined + zero-copy RDMA + GPU staging,最低可降至 0.1%
适用长 prompt / 长输出 / 严 SLO / 异构 GPU / 大模型高并发
不适用超短 + 超短 / 跨 DC 带宽不足 / 单节点 / 工作负载均匀且短

参考资料

  1. Zhong et al., DistServe: Disaggregating Prefill and Decoding for Goodput-optimized LLM Serving, OSDI 2024. https://arxiv.org/abs/2401.09670
  2. Patel et al., Splitwise: Efficient Generative LLM Inference Using Phase Splitting, ISCA 2024. https://arxiv.org/abs/2311.18677
  3. DeepWiki Mooncake 7.5 Prefill-Decode Disaggregation. https://deepwiki.com/kvcache-ai/Mooncake/7.5-prefill-decode-disaggregation
  4. Agrawal et al., SARATHI: Efficient LLM Inference by Piggybacking Decodes with Chunked Prefills, arXiv:2308.16369. https://arxiv.org/abs/2308.16369
  5. SGLang Advanced Features: PD Disaggregation. https://sgl-project.github.io/advanced_features/pd_disaggregation.html