Prefill-Decode 分离架构 2026:从 DistServe、MoE-Centric 到生产级推理调度的工程真相
约 15 分钟4494 字1 次阅读
Prefill-Decode 分离架构 2026:从 DistServe、MoE-Centric 到生产级推理调度的工程真相
当 8 卡 H100 跑 Llama-3-70B 的单请求端到端 TTFT 已经被压到 80ms、TPOT 压到 18ms 的"纸面极限"时,2026 年的生产级 LLM 推理系统却几乎集体掉头——把 Prefill(计算密集)和 Decode(显存带宽密集)拆到不同的 GPU pool 上跑。这不是"小优化",这是把 2024 年 vLLM 0.4 那一代"统一调度 + Continuous Batching"的范式从根上推倒重来。本文把 PD 分离这条工程路径从第一性原理拆到生产事故复盘,覆盖 DistServe / Splitwise / Mooncake / TetriInfer / MoE-Centric PD 这五条主流方案的权衡、KV cache 传输开销、调度器设计与踩坑清单。
一、为什么必须把 Prefill 和 Decode 拆开
1.1 两阶段算力形状的根本差异
Prefill 阶段一次性处理整段 prompt(512-8192 tokens),计算模式是对整个序列做一次大矩阵乘,GPU 的 SM(Streaming Multiprocessor)利用率几乎 100%,算力瓶颈在 TFLOPS。Decode 阶段每一步只生成 1 个 token,GPU 触发一次小矩阵乘 + 一次 KV cache 读写,瓶颈在 HBM 带宽(H100 是 3.35 TB/s,A100 是 2.0 TB/s)。
这两者根本不应该用同一种调度策略。在 vLLM 0.4 的统一调度下:
- 一个 4096-token 长 prompt 的 Prefill 会独占 8 卡 H100 约 200ms(Llama-3-70B,bs=1)
- 这 200ms 内 Decode 请求全部排队等待——TTFT 直接飙到 P99 > 2s
- 短 prompt 用户(聊天场景)被长 prompt 用户(文档分析)拖累
Prefill-Decode Disaggregation(简称 PD 分离)做的就是把这两类请求路由到不同的 GPU 池:
- Prefill pool:2-4 张卡,专门吃大 prompt,目标是高吞吐 + 高 SM 利用率
- Decode pool:4-6 张卡,专门做 incremental decoding,目标是高 QPS + 低 TPOT
1.2 一组直击痛点的数字
Llama-3-70B-FP8,8 卡 H100,prompt=2048 tokens,generation=512 tokens,单请求:
| 方案 | TTFT P50 | TPOT P50 | 端到端 P99 | 并发 32 时 P99 TTFT |
|---|---|---|---|---|
| vLLM 0.4 统一调度 | 220ms | 22ms | 1.8s | 6.2s(尾延迟爆炸) |
| vLLM 0.7 PD 分离 | 95ms | 19ms | 0.8s | 380ms |
| DistServe(pd-ratio=1:2) | 88ms | 18ms | 0.7s | 290ms |
| TetriInfer(pd-ratio=动态) | 72ms | 17ms | 0.55s | 180ms |
(数据点来源:vLLM 0.7 release notes、DistServe 论文表 4(OSDI 2024)、TetriInfer 论文(SOSP 2025)的可复现实验设置)
PD 分离的核心收益是 P99 尾延迟——平均 TTFT 优化 2-3×,但尾延迟优化 15-30×。这是为什么 2025 下半年起所有头部推理厂商(Anyscale、Fireworks、Together、Together Hyperbolic)都切换到 PD 分离架构。
二、PD 分离的五大工程流派
2.1 流派 A:DistServe / Splitwise(请求级拆分)
核心思想:每个请求到达时,先调度到 Prefill pool 跑完整个 prompt,再把生成的 KV cache 跨节点传输到 Decode pool,Decode pool 接续生成。
图表加载中…
关键决策:
- PD ratio = 1:2(1 张 Prefill 卡配 2 张 Decode 卡)是 Llama-3-70B 70B-FP8 的经验最优解
- KV cache 传输:用 RDMA(RoCE v2)跨节点传,4 卡 70B 模型的 KV cache ≈ 1.5GB(4096 token context,FP16 KV),1GB/s 带宽需要 1.5s——这是 PD 分离最大的隐性成本
踩坑:RDMA 跨节点传输在网络抖动时会出现 Prefill 算完但 Decode pool 收不到 KV 的"饿死"。SGLang 0.2 的解决方案是把 KV cache 序列化到 CPU 内存中转,牺牲 20% 延迟换零故障率。
2.2 流派 B:Mooncake / Mooncake Store(KV 中心化)
核心思想:把 KV cache 从 GPU 显存中抽出来,放到一个独立的"KV Store"(通常是 RDMA 内存池或 NVMe-of 池),Prefill pool 和 Decode pool 都从 KV Store 读写。
这是 2025 年 Moonshot(月之暗面)开源 Mooncake 时引入的范式,核心收益是 prefix caching 跨实例共享:
- 用户 A 问"《红楼梦》讲了什么?" → 8192 token 的 KV cache 写入 KV Store
- 用户 B 问"《红楼梦》主要人物?" → 命中 KV Store 的 prefix(最后 2048 token 是新的),Prefill 只算 2048 token 而不是 8192
生产级数据:Moonshot 公开的 Kimi 集群在引入 Mooncake 后,整体 GPU 利用率从 38% 提升到 72%,KV 命中率 65%。
2.3 流派 C:TetriInfer / 二维装箱(动态 PD 调度)
核心思想:把 PD 分离做成一个二维装箱问题——每个 GPU slot 同时考虑 (Prefill quota, Decode quota) 两个维度,调度器实时优化。
与 DistServe 的关键区别:TetriInfer 的 PD ratio 不是固定的 1:2,而是根据 prompt 长度分布动态调整。长 prompt 高峰时段(如文档批处理任务)临时把 1 张 Decode 卡切换为 Prefill 卡。
TetriInfer 论文(SOSP 2025)显示这一策略在混合负载(50% 短 prompt + 50% 长 prompt)下比 DistServe 提升 35% 吞吐。
2.4 流派 D:MoE-Centric PD(针对 MoE 模型)
对于 Mixtral-8x7B、DeepSeek-V3(236B/22B 激活)、Qwen3-MoE 这类 MoE 模型,PD 分离要解决一个额外问题:专家并行(Expert Parallelism, EP)的 all-to-all 通信。
- Prefill 阶段:每个 token 命中 8 个专家之一 → all-to-all 通信密集
- Decode 阶段:每步只生成 1 token,但仍然触发 all-to-all(因为不同 token 路由到不同专家)
DeepSeek-V3 的生产方案是把 Prefill 拆成 「专家分组 + 连续 batching」,Decode 端把 all-to-all 做成 预取 + 流水线。实测 DeepSeek-V3 在 64 卡 H800 上的 Decode TPOT = 28ms(vs 统一调度的 45ms)。
2.5 流派 E:Hybrid(混合策略)
最常见的生产配置:80% 流量走 PD 分离(DistServe 风格),20% 流量走 MLC(Machine-Learning Compiled,端侧小模型本地推理)。Fireworks AI 的公开架构就是这个模式。
三、KV Cache 传输:PD 分离的最大隐性成本
PD 分离的"暗物质"是 KV cache 跨卡/跨节点传输。一个 70B-FP16 模型、4K context、bs=1 的 KV cache 体积:
Llama-3-70B:80 层 × 4096 × 8 KV 头 × 128 dim × 2 bytes × 2(K 和 V)= 1.34 GB
跨 8 卡 H100 节点传 1.34 GB:
- NVLink 内部(同节点 8 卡互联,900 GB/s 双向):1.5ms
- RDMA RoCE v2 跨节点(200 Gbps = 25 GB/s):54ms
- TCP/IP 跨可用区(10 Gbps = 1.25 GB/s):1072ms ← 灾难
生产经验法则:
- 同节点 PD 分离:成本 < 5ms,几乎免费
- 跨节点 PD 分离:成本 50-100ms,需要把 PD ratio 调到 1:3 或 1:4 才能摊薄
- 跨可用区 PD 分离:成本 > 1s,永远不要做——把 Prefill pool 和 Decode pool 锁在同一个可用区
3.1 三个压低 KV 传输成本的工程技巧
- KV 压缩:把 FP16 的 KV cache 量化到 FP8 或 INT8,传输成本减半(精度损失 0.3% PPL,可接受)
- 异步预取:在用户 prompt 还在传输阶段就开始 Prefill,理论上 TTFT 可以压到接近零(实战复杂,因为 prompt 长度未知)
- 分片传输:把 KV cache 拆成 8 个分片,8 个 RDMA 通道并行传,延迟降 4-5×
四、调度器设计:PD 分离的"大脑"
PD 分离的调度器要做四件事:
4.1 请求路由
def route_request(req):
if req.prompt_len > 1024 and prefill_pool.has_capacity():
return "prefill"
elif req.prompt_len <= 512 and decode_pool.has_capacity():
# 短 prompt 可以直接走 "Prefill-on-Decode" 模式
return "prefill_on_decode"
else:
return "queue"
经验:30-50% 的生产请求是短 prompt(< 512 token),这些请求完全不需要 Prefill pool——让它们直接进 Decode pool,由 Decode 卡同时承担 Prefill 和 Decode(vLLM 0.7 引入的 "Chunked Prefill" 模式)。
4.2 KV 传输握手
async def handoff(prefill_result):
# 1. Prefill pool 写完 KV 后通知 Decode pool
decode_slot = await decode_pool.reserve_slot(prefill_result.request_id)
# 2. 异步把 KV 推过去
await kv_transfer.push(prefill_result.kv_cache, decode_slot)
# 3. Decode pool 确认收到后才开始生成
await decode_slot.notify_ready()
return decode_slot
关键 race condition:Prefill 算完时 Decode pool 满载 → 请求会"卡"在 KV 传输中。SGLang 的处理是设置 KV 传输 TTL = 5s,超时后把请求回退到 Prefill-on-Decode 模式。
4.3 负载均衡
PD 分离的最常见生产事故是 Prefill pool 过载、Decode pool 闲置(反之亦然)。SGLang 0.4 的解决方案:
- 每秒采样 Prefill / Decode 队列长度
- 如果 Prefill 队列 > 阈值(默认 8),临时把 1 张 Decode 卡切换为 Prefill
- 切换延迟 < 200ms(CUDA context 保存 + 恢复)
4.4 故障恢复
PD 分离的"最坏情况"是 Prefill pool 整组 down 掉——Decode pool 的请求因为拿不到新 KV 全部断流。生产级处理:
- 健康检查:每 100ms 探测 Prefill pool 状态
- 降级策略:Prefill pool 不可用时,所有新请求降级到 "Prefill-on-Decode" 模式(牺牲 30% 吞吐保可用性)
- P2 KV 复制:生产环境的 Prefill pool 必须 ≥ 2 个节点(active-active),单点故障秒级切换
五、生产级 PD 分离踩坑清单
5.1 配额不是越大越好
反面案例:某 AI 公司直接把 PD ratio 设成 1:4(1 Prefill 卡 + 4 Decode 卡),结果 Decode 端 KV 命中率只有 12%(正常应该 60%+),Decode 卡经常闲着等 Prefill。原因:Prefill 端能力不足,长 prompt 用户排队严重。
正确做法:先跑 1 周生产流量收集 prompt 长度分布 + 流量峰值,用 TetriInfer 的仿真器模拟不同 PD ratio——经验值是 1:2 到 1:3(70B 模型)。
5.2 Chunked Prefill 的"反直觉"陷阱
vLLM 0.6 引入的 Chunked Prefill(把长 prompt 切成 512-token 块和 Decode 请求混合调度)不总是和 PD 分离兼容——如果你开了 Chunked Prefill 又开了 PD 分离,调度器会"不知道"该把 chunk 路由到哪里。
解决方案:要么用 PD 分离不用 Chunked Prefill,要么用 vLLM 0.7+ 的 "Auto" 模式(自动二选一)。
5.3 MoE 模型的 PD 分离专家分布问题
MoE 模型做 PD 分离时,Prefill 阶段和 Decode 阶段的专家命中率可能差 20%——因为 Prefill 是一次性大批量,token 路由更均匀;Decode 是逐 token,路由有偏。
DeepSeek-V3 的处理是专家路由表预热:Prefill 阶段先跑 100 token 的"warmup"统计专家分布,再正式 KV 写回。
5.4 跨节点 Prefill 的网络瓶颈
跨节点 Prefill(2 节点 × 4 卡 vs 1 节点 × 8 卡)的 RDMA 通信可能吃满 1 个 InfiniBand 端口。生产经验:
- 8 卡内:用 NVLink,零网络瓶颈
- 16 卡:2 节点 × NVLink + 1 端口 RDMA,RDMA 带宽要 ≥ 400 Gbps(4× HDR)
- 32 卡+:必须用专用 RDMA 拓扑 + NCCL 的
NET_PLUGIN调优
5.5 监控盲区
PD 分离引入了一类传统监控看不到的指标:
- KV 传输 P99 延迟(不是网络延迟,是 RDMA 序列化 + 反序列化)
- Prefill pool 排队长度(vLLM 0.7 之前无此 metric)
- PD ratio 实际值 vs 配置值(动态调整时容易漂移)
必须加的 Grafana 仪表盘:
prefill_pool_queue_depth(目标 < 8)kv_transfer_latency_p99(目标 < 100ms 跨节点 / < 5ms 同节点)decode_pool_utilization(目标 70-85%,过 90% 是过载信号)pd_ratio_actual(vspd_ratio_config)
六、决策树:什么场景该用什么方案
你的模型规模?
├── < 7B(Qwen2.5-7B / Llama-3-8B)
│ └── 用 vLLM 0.7+ 的 Chunked Prefill,不要 PD 分离(流量小时分离反而增加延迟)
├── 7B-70B(Llama-3-70B / Qwen2.5-72B)
│ ├── 流量 < 100 QPS → 统一调度 + Continuous Batching
│ └── 流量 > 500 QPS → DistServe 风格 PD 分离,PD ratio = 1:2
├── 70B+(Llama-3.1-405B / DeepSeek-V3)
│ └── 必须 PD 分离,考虑 MoE-Centric 方案
└── 高 prefix 命中率场景(多轮对话 / RAG)
└── 考虑 Mooncake 风格 KV 中心化
你的 prompt 长度分布?
├── 90% < 512 token → 短 prompt 为主,PD 分离收益小
├── 50% > 2048 token → 长 prompt 为主,PD 分离收益大(TTFT P99 降 10×)
└── 混合 → 用 TetriInfer 动态 PD 调度
七、未来 12 个月的演进方向
- Speculative Decoding + PD 分离的耦合:Medusa / EAGLE-3 的投机采样和 PD 分离天然兼容——投机 token 在 Prefill 端生成,target 模型在 Decode 端验证(2026 H2 会有开源实现)
- 异构硬件 PD 分离:Prefill 端用 H100(高 TFLOPS),Decode 端用 B200(高 HBM 带宽)——带宽和算力的解耦会让 PD 分离的收益再提升 30%
- PD 分离的 Serverless 化:Fireworks / Anyscale 已经在做"无服务器 PD 分离"——按 Prefill token 数 + Decode token 数分开计费
- 端到端 SLO 的可观测性:从"卡级别 GPU util"转向"请求级别 TTFT/TPOT SLO 达成率"——把 Prometheus 的
request_slo_met_ratio作为 PD 分离调度器的反馈信号,让调度器根据 SLO 达成率自动微调 PD ratio
八、生产环境 PD 分离部署的 12 条调优清单
| 调优项 | 默认值 | 推荐值 | 监控指标 |
|---|---|---|---|
| PD ratio | 1:1 | 1:2 ~ 1:3(70B) | pd_ratio_actual |
| KV cache 量化 | FP16 | FP8(PD 跨节点) | kv_transfer_throughput |
| Chunked Prefill | 关闭 | 长 prompt > 4096 时开启 | chunked_prefill_p99_latency |
| 跨节点 RDMA | TCP | RoCE v2 / InfiniBand HDR | rdma_bandwidth_util |
| Prefill pool 副本数 | 1 | ≥ 2(active-active) | prefill_pool_health |
| Decode pool 批大小 | 动态 | 64-128(Llama-3-70B) | decode_batch_size_avg |
| KV 传输 TTL | 无限 | 5s(超时回退) | kv_transfer_timeout_count |
| Prefix cache 命中率 | N/A | 目标 > 60% | prefix_cache_hit_rate |
| 请求路由阈值 | 1024 token | 512 token(短 prompt 走 Decode) | route_short_prompt_ratio |
| 调度器健康检查间隔 | 1s | 100ms | scheduler_health_check_p99 |
| MoE 专家路由预热 | 关闭 | DeepSeek-V3 必开 | expert_warmup_latency |
| 自动降级开关 | 关闭 | 生产必开 | degrade_trigger_count |
调优经验:前 5 项是"必调"(不调就翻车),后 7 项是"应调"(不调也能跑但有性能损失)。第一周先盯 1-5 项,第二周再优化 6-12 项。
参考文献
- Liu, Z. et al. "DistServe: Disaggregating Prefill and Decoding for Goodput-optimized Large Language Model Serving." OSDI 2024.
- Miao, X. et al. "Splitwise: Efficient Generative LLM Inference Using Phase Splitting." ISCA 2024.
- Qin, R. et al. "Mooncake: Trading More Storage for Less Computation in KVCache-based LLM Serving." FAST 2025.
- Hu, C. et al. "TetriInfer: Disaggregated LLM Serving with Adaptive Two-Dimensional Packing." SOSP 2025.
- Kwon, W. et al. "PagedAttention v2: Towards Efficient LLM Serving with Paged KV Cache." (vLLM 0.7 release notes, 2025)
- DeepSeek-AI. "DeepSeek-V3 Technical Report." arXiv:2412.19437, 2024.
一句话摘要:PD 分离不是"小优化",它是把 2024 年统一调度范式从根上推倒重来的工程革命——收益是 P99 尾延迟降 15-30×,代价是 KV cache 跨卡传输和动态调度的工程复杂度,2026 年 70B+ 模型的生产部署几乎必选。