PD 分离与推理架构设计¶
更新日期:2026-04-15
一、为什么要 PD 分离¶
flowchart LR
user["请求"]
pre["Prefill<br/>计算密集<br/>(读 prompt)"]
kv["KV Cache<br/>传输"]
dec["Decode<br/>带宽密集<br/>(逐 token 生成)"]
out["Output"]
user --> pre --> kv --> dec --> out
classDef stage fill:#fff,stroke:#cc785c,color:#1a1a1a;
class user,pre,kv,dec,out stage
| 阶段 | 瓶颈资源 | 适合硬件 |
|---|---|---|
| Prefill | 算力(FLOPs) | H100 / B200(高 TFLOPS) |
| Decode | 显存带宽(HBM) | H200 / B200(更大 HBM) |
混合部署时同 GPU 同时做两件冲突的事 → 互相挤压:
- Prefill 占满 SM 时,Decode 卡顿(latency 高)
- Decode 占满 HBM 带宽时,Prefill 慢(throughput 低)
- batch 越大冲突越严重
PD 分离让两类请求各跑各的 GPU,整体吞吐提升 2-4×(DistServe paper 实测)。
1.1 根本原因¶
混合部署时,同一 GPU 要同时做两件本质冲突的任务。分离后,每类 GPU 做自己最擅长的事。参考 DistServe (Zhong et al., OSDI 2024)。
二、PD 分离核心技术¶
2.1 KV Cache 传输¶
class PDDisaggregator:
def __init__(self):
self.prefill_workers = [PrefillWorker(gpu=i) for i in range(n_prefill)]
self.decode_workers = [DecodeWorker(gpu=i) for i in range(n_decode)]
self.router = Router()
async def serve_request(self, request):
# 1. Router 分配
prefill_worker = self.router.assign_prefill(request)
decode_worker = self.router.assign_decode(request)
# 2. Prefill
kv_cache = await prefill_worker.prefill(request.prompt)
# 3. KV Cache 传输 (关键!)
# 选项 A: 通过 NCCL (GPU 直接传)
# 选项 B: 通过 CPU + IB (慢)
# 选项 C: 异步流水线 (边计算 layer_i+1 边传 layer_i)
await transfer_kv_cache(prefill_worker, decode_worker, kv_cache)
# 4. Decode
async for token in decode_worker.decode(request, kv_cache):
yield token
2.2 流水线传输优化¶
# 朴素做法: prefill 完成 → 全量传输 → 开始 decode
# 问题: 传输时 GPU 空闲,首 token 延迟高
# 优化: 逐层流水线
def pipelined_transfer(prefill_gpu, decode_gpu, n_layers):
for layer_i in range(n_layers):
# Prefill 计算 layer_i
kv_i = prefill_gpu.compute_layer(layer_i)
# 异步传输 layer_i 的 KV (不阻塞下一层计算)
async_send(kv_i, decode_gpu)
# 同时启动 layer_i+1 的计算
# 最后一层完成时, 前面的 KV 已经传输完毕
# 等最后一层传输完成
await wait_all_transfers()
三、架构设计¶
3.1 比例配置¶
| 工作负载 | Prefill : Decode 比例 | 备注 |
|---|---|---|
| 闲聊(短 prompt 短回答) | 1:3 - 1:5 | Decode 主导 |
| 代码 / 长文本生成 | 1:2 | Decode 略多 |
| RAG(长 prompt 短回答) | 1:1 | Prefill 偏重 |
| 代码 review(很长 prompt) | 2:1 | Prefill 主导 |
| Reasoning model(长 thinking) | 1:5+ | Decode 极重 |
实操:先 1:1 起步监控两侧排队,按队列深度动态扩缩容。
3.2 同步 vs 异步 PD¶
flowchart LR
subgraph sync["同步 PD"]
s1["请求<br/>Prefill"] --> s2["等 KV<br/>传完"] --> s3["Decode<br/>开始"]
end
subgraph async["异步 PD"]
a1["请求<br/>Prefill 开始"] --> a2["分层<br/>边算边传"]
a2 --> a3["Decode<br/>(部分 KV 到达即可启动)"]
end
classDef stage fill:#fff,stroke:#cc785c,color:#1a1a1a;
class s1,s2,s3,a1,a2,a3 stage
| 维度 | 同步 PD | 异步 PD |
|---|---|---|
| 实现复杂度 | 低 | 中-高 |
| TTFT | 高(等 KV 全到) | 低(部分 KV 到即可启动) |
| 吞吐 | 中 | 高 |
| 故障恢复 | 简单(重试) | 复杂(中间状态) |
| SGLang 默认 | ❌ | ✅ |
| vLLM 默认 | ✅ | 实验性 |
工业级 deploy 都用异步 PD(SGLang 路线)。同步只在调试 / 简单原型用。
四、SGLang 的 PD 分离实现¶
SGLang 在 2025 年原生支持 PD 分离,是目前开源方案中最成熟的。参考 SGLang Disaggregation。
4.1 架构¶
# SGLang 启动 PD 分离集群
# Prefill workers:
python -m sglang.launch_server \
--model-path deepseek-v3 \
--disaggregation-mode prefill \
--tp 4 \
--host 0.0.0.0 --port 30000
# Decode workers:
python -m sglang.launch_server \
--model-path deepseek-v3 \
--disaggregation-mode decode \
--tp 4 \
--host 0.0.0.0 --port 30001 \
--prefill-urls http://prefill_worker_1:30000,http://prefill_worker_2:30000
# Client 直接连 decode worker, 由它协调 prefill
4.2 性能数据¶
公开 benchmark(SGLang + DeepSeek-V3 + AMD MI300X):
| 配置 | TTFT | TPOT | Throughput |
|---|---|---|---|
| 混合部署(baseline) | 850ms | 35ms/tok | 1× |
| PD 分离同步 | 720ms | 28ms/tok | 1.6× |
| PD 分离异步 | 510ms | 22ms/tok | 2.4× |
异步 PD 在 TTFT + 吞吐两个维度都显著优于混合部署。
4.3 DeepSeek-V3 PD 实际数据¶
在 96×H100 集群上,SGLang PD 分离达到:52,300 input tok/s + 22,300 output tok/s per node。参考 SGLang DeepSeek Blog。
五、vLLM 的 PD 分离(llm-d)¶
vLLM 通过 llm-d 项目提供 PD 分离支持,但成熟度不如 SGLang。
# vLLM 的实验性支持
vllm_args = {
'--kv-transfer-config': '{"kv_connector": "PyNcclConnector"}',
'--disaggregated-serving': True,
}
# 数据: vLLM + llm-d 0.3 on H200
# 32-way EP: ~2.2K tok/s/GPU
# 96-way EP: ~2.0K tok/s/GPU
六、PD 分离的工程挑战¶
| 挑战 | 描述 | 为什么这是难题 | 缓解方案 | 实践建议 |
|---|---|---|---|---|
| KV Cache 传输带宽 | 大上下文的 KV Cache 可达数 GB(如 128K context + 70B 模型,KV Cache 约 20-40GB) | KV 传输延迟直接加到 TTFT 上;若传输耗时 >100ms,PD 分离的 TTFT 优势被抵消 | 用 NVLink(900GB/s)或 InfiniBand(400Gb/s)而非 TCP(~10GB/s);逐层流水线传输(边算边传) | 同机架内走 NVLink/NVSwitch 最优;跨机架至少用 IB HDR/NDR;KV Cache 可用 FP8 量化减半传输量 |
| Prefill/Decode 负载不均 | 不同时段的请求模式变化(白天 RAG 密集 prefill 多,晚上闲聊 decode 多) | 静态比例配置无法适应流量波动,导致一侧 GPU 空闲另一侧排队 | 动态调整 pool 大小:监控 prefill/decode 队列长度,按比例弹性伸缩 | 建议设置自动伸缩策略:队列深度 >阈值时扩容,空闲 >5 分钟时缩容;保留 10-20% buffer 应对突发 |
| 故障恢复 | Prefill worker 挂掉时,已分配给它的请求和正在传输的 KV Cache 都丢失 | 单体部署只需重试请求即可;PD 分离中请求状态跨两个 worker,需要协调恢复,否则用户看到中断 | Checkpoint + 重试:decode worker 检测 prefill 超时后自动将请求重新分配到健康的 prefill worker | 设置 prefill 超时阈值(如 10s);实现 prefill 幂等性(重复 prefill 不影响正确性);多副本 prefill pool 提升容错 |
| 延迟敏感的短请求 | Prompt <100 token 的短请求,PD 分离的 KV 传输开销可能超过 prefill 本身耗时 | 短 prompt prefill 仅需 1-5ms,但 KV 传输至少 1-10ms(取决于网络),PD 分离反而增加延迟 | 路由策略区分长短请求:短请求走混合模式(同 GPU 上 prefill+decode),长请求走 PD 分离 | 设置 prompt 长度阈值(建议 512 token);低于阈值的请求直接在 decode worker 上做 prefill,跳过传输 |
| 多模型共享集群 | 不同模型(如 7B 和 70B)的 KV Cache 维度、层数、精度不同,无法混用 | PD 分离的 KV 传输协议需要知道 KV 格式(head_dim, n_layers, dtype);混用会导致数据错位 | 每个模型维护独立的 prefill/decode pool;或设计统一 KV 序列化协议(header 描述格式) | 多模型场景建议用 Kubernetes 做 pool 隔离,每个 Deployment 对应一个模型的 PD 集群 |
七、何时 PD 分离真正值得¶
flowchart TB
start["要部署 LLM"]
q1{"QPS"}
q2{"prompt 长度"}
start --> q1
q1 -->|"<100"| simple["混合部署<br/>简单维护"]
q1 -->|"100-1000"| q2
q1 -->|">1000"| pd["强制 PD 分离<br/>+ 异构硬件"]
q2 -->|"短 (<512 tok)"| simple2["混合部署<br/>PD 收益有限"]
q2 -->|"长 (>512 tok)"| pd2["PD 分离<br/>SGLang 异步"]
classDef stage fill:#fff,stroke:#cc785c,color:#1a1a1a;
classDef decision fill:#f5f3eb,stroke:#bdb9ab,color:#1a1a1a;
class start,simple,simple2,pd,pd2 stage
class q1,q2 decision
生产建议: - 小流量 (<100 QPS):不用 PD 分离,简单部署 - 中流量 + 短 prompt:可选,收益有限 - 大流量 + 长 prompt (RAG/代码):强烈推荐 PD 分离
八、未来方向¶
| 方向 | 描述 | 为什么值得探索 | 当前进展与挑战 | 实践建议 |
|---|---|---|---|---|
| 多级 PD | Prefill → Chunked Decode → Final Decode,将 decode 进一步拆分为"前几十 token 快速生成"和"后续批量生成" | 前几个 token 对用户体验最关键(TTFT),后续 token 可以批量优化吞吐;多级拆分允许不同优先级调度 | 调度复杂度显著增加;目前 SGLang/vLLM 尚无原生支持,需自行实现中间层 | 先观望;当单级 PD 的 TTFT 无法满足 SLA 时再考虑多级拆分 |
| 跨机房 PD | Prefill 在算力密集机房(如 H100 集群),Decode 在内存优化机房(如 H200/CPU 集群) | 不同机房可以采购不同硬件配置,整体 TCO 降低 20-40%;算力/内存需求的异构性天然适合异地部署 | 跨机房网络延迟(通常 1-10ms)会直接加到 KV 传输时间;需要高带宽专线(至少 100Gbps)和可靠的故障转移 | 仅在自有数据中心且两个机房有专线互联时考虑;公有云上跨 region 延迟太高不划算 |
| 异构 PD | Prefill 用高算力 GPU(H100/H200),Decode 用高性价比 GPU(L40S/A100)甚至专用推理芯片 | Decode 几乎不用算力,用旗舰 GPU 做 decode 是浪费;L40S 价格约为 H100 的 ⅓ 但 HBM 带宽够用 | 不同 GPU 的驱动/CUDA 版本需要兼容;KV Cache 的精度和格式需要在异构 GPU 间保持一致 | 高性价比路线:Prefill 用 H100 SXM5 + Decode 用 L40S 48GB,整体成本降低约 30% |
| Cache 持久化 | KV Cache 从 GPU 卸载到 CPU DRAM 或 NVMe SSD,复用历史会话(如多轮对话中前几轮的 KV) | 多轮对话中每轮都重新 prefill 历史 context 极其浪费(相同前缀反复计算);持久化 KV 后只需 prefill 增量部分 | CPU/SSD 读取带宽(约 10-50GB/s)远低于 HBM(约 2-5TB/s),KV 加载延迟可能达数十 ms;需要 LRU 淘汰策略管理缓存容量 | 对多轮对话和 RAG(相同文档库的 KV 可复用)ROI 最高;建议 CPU DRAM 做热缓存 + SSD 做冷缓存的两级架构 |
参考文献¶
-
[1] Zhong et al. DistServe: Disaggregating Prefill and Decoding. OSDI 2024. 论文
-
[2] Patel et al. Splitwise: Efficient Generative LLM Inference Using Phase Splitting. 2023. 论文
-
[3] DuetServe: Harmonizing Prefill and Decode. 2025. 论文
↑ 上级 · G. 推理与部署