PD 分离实战:Prefill/Decode 拆池、配比拐点与容量规划

PD 分离实战:Prefill/Decode 拆池、配比拐点与容量规划

Deng YongJie's blog 5 2026-08-23

PD 分离实战:Prefill/Decode 拆池、配比拐点与容量规划

大模型推理基础设施实战系列 · 第 2 篇

1. 背景与问题:两种性格迥异的工作负载,为什么要抢同一批 GPU

Prefill 和 Decode 是两种资源性格完全不同的负载:

Prefill Decode
计算特征 大批量矩阵乘,计算密集 低批量常受访存限制;大批量也可能受计算或通信限制
持续时间 长输入可达数百秒 每步几十毫秒,持续整个生成
对延迟的影响 决定 TTFT 决定 TPOT/ITL
批处理收益 受形状、算子和通信影响 增大 batch 可能提高效率,也增加排队和工作区

长 prefill 与 decode 混跑可能造成相互干扰。项目 Kimi-K3 的历史双 PP4 配置曾观察到 538.91 秒流式停顿,但后续 0.56 秒结果来自 FlashInfer、分层、分块和准入等组合调整,并未拆成 P/D 池。该对照不能充当 PD 分离的收益证据。

PD 分离将 Prefill 和 Decode 放到独立实例,目标是减少计算调度干扰,并允许两侧分别扩容;收益需要连同 KV 传输与排队成本实测。

第二个动机是独立扩缩容:长文档高峰期需要更多 prefill 算力,闲聊高峰期需要更多 decode 容量。统一部署只能整体加副本,PD 分离后可以单独加 P 或加 D。

2. 技术选型:原生引擎 + 官方 Gateway,而不是自研转发层

我们评估过三条路线:

路线 评估结论
自研代理层做 PD 调度 曾用于快速验证,但准入语义、取消传播、生命周期管理都要自己维护,项目最终采用官方组件;这项选型不等于通用的技术禁令
Ray Serve + SGLang 当时的 Ray SGLang 适配未接入 PD,引入它只解决副本管理,不解决 PD 路由,放弃
原生 SGLang PD + 官方 Model Gateway 引擎内建 P/D 角色、bootstrap 配对与 KV 传输,Gateway 提供登记、选路、健康检查——采用

两个刻意的保守决定:

  1. 同构 P/D:首版 P 和 D 使用相同的 TP/PP 组合与制品,通过增减完整副本切换 1P1D、2P1D、3P1D;异构 P/D(不同 TP/PP)不在第一轮引入。
  2. 副本增减,不做在线角色翻转:上游有"排空后运行时角色切换"的 PR,但其验证集中在特定 AMD 纯 TP 组合;另一个 SM120 + PP8 候选设计未因此获得验收,首版不开启。

3. 架构设计:共享池共享的是"候选实例集合"

PD 分离共享池架构

数据流:客户端 → 有界接入(鉴权 + 限流)→ Gateway 选择一个健康 P 和一个健康 D → bootstrap 交换配对元数据,P 完成 prefill 后通过配置的传输后端把 KV/状态传给 D → D 持续 decode,流式结果经 Gateway 返回。

架构上有四条容易理解错的规则,都来自实践教训:

① 登记的单位是完整实例,不是 GPU。 例如一个跨机 TP8×PP8 候选副本只作为一个服务实例登记;下文矩阵实际使用 TP8/PP1,不能把 64 个 rank 登记成 64 个 P。所有 rank 健康、模型就绪、准入通过后才接业务。

② 准入是全生命周期的。 “HTTP 连接释放"不等于"GPU 工作结束”:客户端断开必须关联取消 D 并验证资源最终释放。我们曾观察到 Gateway 配置并发 16、queue=0,实际却出现大量在途请求和 D 预分配等待——许可是否持有到请求结束,必须实测验证,不能按参数名声称严格封顶。

③ 在途请求保持原配对。 已生成一半的请求不承诺无损迁移;失败或状态未知的生成请求不自动重放——这是账本纪律(第 5 篇详述)。

④ 扩缩容走完整排水流程。 缩 P 的标准动作:退出新请求候选(先验证该 API 不中断在途请求)→ 等在途请求与 KV 传输收尾(按 request ID 逐条核,覆盖全部 rank,不只看 GPU busy)→ 两次独立空状态检查 → 按 PID/容器身份精确停止并验证 cgroup 为空 → 持久化新拓扑 → 留回退条件。注意:被摘除 P 的独有前缀缓存不会自动迁移,缩容后要预期冷输入短暂变差。

4. 已测最高点与配比扫描:128 卡 12P4D 的 708 万总 TPM

4.1 矩阵坐标:64 个已测点中的最高观察值

我们对两款 Flash 模型跑过 8 种 P:D 组合 × 4 种负载的完整矩阵(各 32 点、2,520/2,520 请求全部完成)。硬件均为 RTX 6000D;模型分别为 GLM-5.3-Flash 与 DeepSeek-V4.1-Flash,均为当时固定 SGLang 制品。最高总 TPM 观察值来自 GLM 的 12P4D:

模型 负载(输入/输出) 已测配置 C / N 总 TPM 输出 TPM TTFT p99
GLM 系 80K / 600 12P4D / 128 卡 48 / 192 7,079,236 52,691 33.72 s
GLM 系 380K / 7000 8P4D / 96 卡 16 / 32 2,360,829 42,701 81.50 s
DeepSeek 系 80K / 600 12P4D / 128 卡 48 / 192 1,610,132 11,982 156.36 s
DeepSeek 系 380K / 7000 12P4D / 128 卡 16 / 32 676,175 12,230 434.77 s

128 卡 12P4D、48 并发下,GLM 系模型达到 708 万总 TPM、输出 5.27 万 TPM、TTFT p99 为 33.72 秒——这是该矩阵的最高观察值,尚非饱和产能。80K/600 两行相差约 4.40 倍,但模型、算子和制品路径共同变化,不能仅归因于注意力架构;380K 两行卡数也不同。各行均为 ignore_eos 合成容量轨,不能承诺正文质量或延迟 SLO。

4.2 配比扫描实验设置

以 DS 系模型做精细配比扫描:

  • 模型与并行:每副本 TP8/EP8/PP1 共 8 卡;12 台 96 卡固定预算;
  • 负载:合成容量轨(ignore_eos),实际输入 160K/224K/288K token,固定输出 1300 token;
  • 计量:总 TPM = 60 × 成功请求的实际 (prompt+completion) token ÷ 整轮墙钟;
  • 样本:160K 点三轮确认,224K/288K 为单轮观察。

4.3 配比扫描结果:吞吐随 P 占比上升,边际收益递减

P:D 配比对总吞吐的影响

160K/1300 的完整并发扫描揭示了更多细节:

P/D(比例) 并发 C 总 TPM 输出 TPM TTFT p99 备注
8P4D(2:1) 8 688,345 N/A¹ 69.3 s
8P4D(2:1) 16 1,080,916 N/A¹ 141.6 s
8P4D(2:1) 32 1,091,671 N/A¹ 277.3 s C16→C32 吞吐几乎不动
8P4D(2:1) 64 1,091,671 N/A¹ 546.9 s 再加并发只涨延迟
10P2D(5:1) 16 1,301,401 N/A¹ 141.3 s
10P2D(5:1) 32 1,306,779 N/A¹ 254.3 s C16→C32 仅 +0.41%,TTFT +80%

¹ 本表沿用旧窗口报告的总 TPM 摘要,尚未从该组逐请求账本独立复算输出 TPM,不按目标输入/输出比例补造。完整窗口局限见来源报告。

三轮确认:3:1/C24 中位 1,220,736 TPM(极差/中位 3.96%),5:1/C32 中位 1,306,779(极差 0.82%),中位数之比 +7.05%。224K、288K 点上 5:1 相对 3:1 分别高 9.32%、13.68%。

结论不是"5:1 最优",而是:

  • 提高 P 占比对长输入吞吐有正向收益,但 C16→C32 的边际收益已接近零——该点继续加并发没有明显吞吐收益;排队发生在哪一侧还需阶段计时证明;
  • 5:1 的 TTFT p99(252–254s)显著差于 3:1(204–209s)——两行并发也由 C24 变成 C32,不能将延迟差异全部归因于 D 数量。配比是吞吐与延迟的权衡,应由业务 SLO 决定;
  • 352K/1300 只有 8P4D 成功(1,036,437 TPM),其他比例出现 HTTP503 和协议失败——记为"该配置尝试失败",不能写成"352K 以上全部失败",未尝试的格子更不能画进曲线。

5. 难点与解决:参数探索中的 OOM 与归因纪律

380K 输入在基线参数(chunk/max-prefill 4096、static 0.60、context 1048576)下直接报 KV 相关错误。我们做了 T1–T6 六组参数探索(均为 8P4D/C32 探针):

实验 变更 380K 探针结果 当时的解读 vs 复核结论
T1 chunk 16384 1/4 成功,其余 OOM 大 chunk 下工作区不足的线索
T2 T1 + static 0.75 0/4 OOM 提高 static 未改善,不继续盲增
T3 chunk 16384 + static 0.50 1/4 仍 OOM 降 static 尚不足
T4 T3 + context 524288 1/4 OOM 需核对 KV 池与 indexer 工作区
T5 T4 + MR2 0/4,OOM + 传输异常 新变量与故障混合,不能认定 MR2 有害
T6 chunk 8192 + static 0.50 + context 524288 + MR4 完成窗口:1,016,882 TPM 相对基线是多变量组合

OOM 堆栈指向稀疏注意力 indexer(_low_ratio_index_topk_extend)单次约 5.5 GiB 的分配——支持"chunk 过大挤占工作区"的假设,但"chunk 减半即修复根因"仍需同配置对照证明。

6. 效果与总结

PD 分离在我们实践中的定位:

收益 证据 边界
最高观察值 128 卡 12P4D 达 708 万总 TPM(80K/600,GLM 系矩阵最佳) 单模型单负载峰值,不可跨模型移植
隔离调度干扰 P/D 运行在独立实例 本文没有同资源、同负载的拆分前后停顿对照,不报收益倍数
P/D 独立扩缩 1P1D→2P1D→3P1D 只需增减完整副本 扩 P 只处理新流量;缩 P 不增加 D 算力
配比可按负载调整 96 卡上 5:1 比 3:1 高 7.05%(160K) 单模型单负载结论,不可移植

给准备做 PD 分离的团队四条建议:

  1. 先把单实例和 1P1D 做正确(鉴权、取消、传输失败、排水),再谈池化;池化收益的对照要求"固定组 vs 共享池、同 GPU 总数",1P1D 本身证明不了池化收益;
  2. 配比扫描固定 GPU 总预算,同时报告总 TPM、TPM/卡、TTFT——只报总 TPM 会把"加卡"伪装成"配比优";
  3. 每个模型的并发与配比自己扫,并区分"已测失败"与"未尝试";
  4. 多变量参数包有效就先当"候选包"用,别急着写成"某参数的最佳实践"。

下一篇深入 PD 分离的另一半——KV 如何跨实例、跨介质流动:GPU→内存→SSD 三级 KV 缓存:Mooncake 分层实战与跨引擎复用陷阱。

系列目录

  1. KV Cache 与前缀缓存:从原理到 99.9% 命中率的生产实践
  2. PD 分离实战:Prefill/Decode 拆池、配比拐点与容量规划(本篇)
  3. GPU→内存→SSD 三级 KV 缓存:Mooncake 分层实战与跨引擎复用陷阱
  4. Kimi-K3 原始 MXFP4 权重512K 长上下文生产部署:双机 16 卡 TP8×PP2 与 CUDA Graph 调优
  5. 大模型压测方法论:如何让性能数据可信
  6. 推理集群可观测性:64 卡监控体系与缓存证据链
  7. MiniMax-H3视频生成服务化:ComfyUI 多 GPU 编排与长任务生命周期
  8. 掉卡排查实录:RTX 5070 反复 Xid 79,如何排查建立证据链
  9. GPU对调定位法:8 卡 RTX 6000D 掉卡,如何用对调缩小故障范围
  10. CUDA 802 不是 NCCL 网络故障:H20 NVSwitch 物理链路排查实录
  11. DeepSeek-V4.1-Flash 双机 16 卡 H20 部署压测:TP8×DP2 与跨机 EP 的边界

数据声明:本文数据来自受控内部测试环境;380K/7000 等重负载成绩为合成容量轨(ignore_eos),不代表正文质量或可售产能。