7.5 解耦架构的挑战与配比:调度复杂度与 P/D 池资源推导
解耦引入的调度复杂度、P/D GPU 池配比推导(基于负载特征)、池内利用率与弹性、何时该用/不该用解耦的决策框架
解耦不是免费午餐:7.2 说过”解耦不免费”,这一节把账单列清楚——调度复杂度(两跳调度、状态传递、失败处理)和资源配比(P/D 池各多少卡)。配比是解耦部署最实操的问题:卡给少了 P 池排队 TTFT 爆,给多了浪费。本节给出基于负载特征的配比推导方法,以及”该不该解耦”的决策框架。
📑 目录
- 1. 解耦引入的复杂度全景
- 2. 调度复杂度:从一跳到两跳
- 3. 配比推导:从负载特征到 P/D 卡数
- 4. 配比公式的实操:一个完整例子
- 5. 池内效率与弹性伸缩
- 6. 决策框架:什么时候该解耦
- 7. 解耦的运维风险清单
- 📝 总结
- 🎯 自我检验清单
- 📚 参考资料
1. 解耦引入的复杂度全景
混合架构是一个调度器、一个池;解耦变成两个调度器、两个池、一条链:
混合: [请求] → [调度器 → GPU 池]
解耦: [请求] → [P 调度器 → P 池] ──KV──→ [D 调度器 → D 池] → 响应
└── 路由/队列管理 ──┘ └── 状态协调 ──┘
新增的复杂度维度:
| 维度 | 混合架构 | 解耦架构 |
|---|---|---|
| 调度器 | 1 个 | 2 个(P/D 各自)+ 路由层 |
| 状态 | 单实例内存态 | 跨实例状态(请求的 KV 归属、进度) |
| 失败模型 | 进程挂 = 请求挂 | P 挂了 D 还在等 KV(悬空请求) |
| 调参面 | TP/并发/batch | 每池各一套 + 传输配置 + 配比 |
💡 提示:复杂度不是”理论上的多”,而是”排错时的多”——混合架构里一个请求的完整生命周期在一份日志里;解耦后要跨两个实例、一条传输链路才能还原一条请求。生产排错工具(trace ID 贯穿、统一日志)在解耦部署里是必需品而非可选项。
2. 调度复杂度:从一跳到两跳
2.1 路由层
请求到达后,路由层决定送哪个 prefill 实例。路由要考虑:
- 负载均衡:P 池各实例的队列深度
- 亲和性:相同前缀的请求尽量送同一实例(利用 APC/前缀缓存,7.3 的 KV 复用)
- 预测性:按请求特征(prompt 长度)预估 P 实例负载
2.2 D 实例的选择
KV 传完后,还要决定 KV 送哪个 decode 实例——这引入一个混合架构没有的问题:KV 的”位置”决定了 D 实例(KV 在哪,decode 就得到哪),调度自由度下降。高端方案(Mooncake 的池化 KV)把 KV 和 D 实例解耦,让 D 实例自由选择——但代价是更重的 KV 存储层。
2.3 失败处理
- P 实例失败:已传出的 KV 悬空——需要”孤儿请求”回收机制(D 侧超时释放)
- D 实例失败:请求的 KV 还在池里,可以重试路由到另一个 D 实例(解耦的隐藏红利:D 层天然可重试)
- 传输失败:需要重传语义(幂等 insert / 超时 drop_select)
📌 关键点:解耦把”请求生命周期”从单进程扩展为分布式事务——调度器必须处理部分完成状态。生产解耦系统的成熟度,很大程度看失败处理是否完备:孤儿 KV 回收、超时、重试、幂等。这些在混合架构里根本不存在,是解耦的”隐性税”。
3. 配比推导:从负载特征到 P/D 卡数
3.1 核心思路
P/D 池的配比由负载特征决定,两个关键量:
- prefill 负载:请求速率 × 平均 prompt 长度(token/s 的 prefill 处理需求)
- decode 负载:并发请求数 × 平均输出速率(token/s 的 decode 需求)
3.2 需要的输入
| 变量 | 含义 | 来源 |
|---|---|---|
| 请求速率(req/s) | 压测/生产监控 | |
| 平均 prompt 长度 | 负载画像 | |
| 平均输出长度 | 负载画像 | |
| 单卡 prefill 吞吐(token/s) | 实测(TP 配置下) | |
| 单卡 decode 吞吐(token/s) | 实测(并发下) |
3.3 推导公式
P 池卡数(prefill 是”吞吐型”负载,按 token/s 计算):
D 池卡数(decode 是”并发型”负载,按并发请求数计算):
等价地(用请求速率表示):
💡 提示:两个公式长得一样,但含义不同——P 池按”每请求必付”的 prompt 吞吐配卡,D 池按”输出总量”配卡。两者的比值就是 P/D 配比:
配比只由”负载长度比 × 单卡能力比”决定——负载画像一变,配比就要重算。这就是为什么”配比是动态的”。
3.4 峰值余量
公式里的”利用率目标”必须 < 100%:
- 预留 SLO 余量(7.4 的 P99 成本)——建议 60-80%
- 突发缓冲——建议额外 20-30%
- P 池余量更关键:P 池超载直接打 TTFT(D 池超载只打尾部 TPOT),且 P 池突发更难预测(prompt 长度方差大)
4. 配比公式的实操:一个完整例子
场景:对话服务,峰值 20 req/s,平均 prompt 2K token,平均输出 500 token。单卡实测(TP=1):prefill 吞吐 3K token/s(4K 长 prompt 场景),decode 吞吐 800 token/s(并发 64)。
P 池:
D 池:
配比 ≈ 1:1,总 37 卡——如果负载变成”长文档总结”(prompt 8K、输出 200):
配比变成 11:1——同一个服务,负载特征一变,最优配比差一个数量级!
📌 关键点:这个例子说明两件事:① 配比必须按自己的负载画像算,抄别人的配比没用;② 负载画像会变,配比要定期重算。工程实践:把配比推导做成脚本(输入监控数据、输出建议配比),每季度/负载变化时重跑。
5. 池内效率与弹性伸缩
5.1 池内效率的差异
- P 池:burst 型负载(prompt 到达波动大),效率低时卡闲置严重——但 P 池必须留余量(TTFT 敏感)
- D 池:稳态型负载(输出持续生成),利用率更稳定——但 decode 的并发一旦降下来(低峰),卡同样闲置
5.2 弹性策略
| 策略 | 做法 | 适用 |
|---|---|---|
| P 池弹性 | 低峰缩 P 池(合并到 D 池),高峰扩回 | prompt 峰谷明显的服务 |
| D 池弹性 | 按并发数伸缩(K8s HPA 按排队长度) | 输出量随用户量波动 |
| 角色互换(TaiChi 思路) | 实例可在 P/D 角色间切换 | 负载特征变化大的多租户 |
5.3 弹性与 KV 的冲突
弹性伸缩和 KV 传输有冲突:D 实例缩容时,它持有的 KV 怎么办? 池化 KV(Mooncake/LMCache)能解耦”KV 存储”与”D 实例”,让弹性无痛;非池化方案(直接 P→D 传输)缩容会丢 KV,需要重 prefill 或优雅排空。
💡 提示:这解释了为什么生产解耦系统(Mooncake、LMCache)都往”KV 池化”方向走——池化不只是为了复用,更是为了让池子可伸缩。KV 从”跟着实例走”变成”存在池里”,实例就可以随意增删。
6. 决策框架:什么时候该解耦
把 7.1-7.5 的知识收拢成一个决策清单:
解耦的强信号(占 2 条以上值得做):
- P95/P99 TPOT 抖动明显(7.1 的干扰特征)
- 长 prompt 占比高(>2K token 的请求多)
- TTFT 和 TPOT 都有严格 SLO(P95 级)
- 峰值并发下 D 池排队(尾部持续劣化)
- 有”独立调 TTFT/ITL”的诉求(模型/卡型要分开升级)
解耦的反对信号(占 2 条以上谨慎):
- 短 prompt + 短输出(传输摊薄不了)
- 单实例吞吐已满足 SLO(问题根本不存在)
- 无严格分位点 SLO(均值能交代就行)
- 网络 < 200Gb/s(传输吃掉收益,7.3)
- 运维能力不足(跨实例排错、失败处理是硬门槛)
📌 关键点:决策的最终标尺还是 Goodput——在目标负载和 SLO 下,解耦前后的 Goodput 差多少。先按 7.4 的方法量出混合架构的 Goodput 基线,再估算解耦后的 Goodput 上限(用本节公式配卡),差值就是解耦的”投资回报率”。数字说话,别拍脑袋。
7. 解耦的运维风险清单
即使决策通过,运维上还有这些坑要提前排:
| 风险 | 缓解 |
|---|---|
| 跨实例请求追踪困难 | 统一 trace ID(路由层生成,贯穿 P/D) |
| 孤儿 KV 泄漏(P 挂/超时) | 过期回收任务(TTL 扫描) |
| 传输链路故障 | 传输健康检查、自动重路由 |
| 配比过时(负载漂移) | 配比脚本定期重算 + 弹性伸缩 |
| 版本不一致(P/D 引擎版本不同) | 发布流程强制同版本 |
| 扩容时 KV 分布不均 | 池化 KV / 优雅排空后扩容 |
💡 提示:解耦部署的验收不只是”功能通”,要专门演练故障场景:P 池挂一台卡、传输断 30 秒、D 池缩容一半——每个场景都要有明确的行为预期(重试?降级?拒绝?)。把故障演练写进发布清单,解耦才敢上生产。
📝 总结
- 复杂度税:两跳调度、跨实例状态、孤儿 KV、分布式失败处理——排错成本翻倍
- 配比公式:,,配比 = 负载长度比 × 能力比
- 配比是动态的:负载画像一变配比就差一个数量级,要脚本化重算
- 弹性与 KV 冲突:池化 KV(Mooncake/LMCache)是弹性伸缩的前提
- 决策框架:强信号/反对信号对照 + Goodput 差值算 ROI
- 运维:trace ID、孤儿回收、故障演练三件套
🎯 自我检验清单
- 解耦引入的三类复杂度是什么?为什么排错成本翻倍?
- P/D 配比公式的每个变量怎么获取?配比为什么随负载变化?
- 给定负载(R、Lp、Ld、单卡能力)口算配比?
- 为什么”P 池余量比 D 池余量更关键”?
- 弹性伸缩与 KV 传输的冲突是什么?池化 KV 为什么能解?
- 用决策清单评估你的服务该不该解耦?
- 解耦上生产前要演练哪三类故障?
📚 参考资料
- Zhong et al., DistServe(放置与调度算法)(https://www.usenix.org/conference/osdi24/presentation/zhong-yinmin)
- Qin et al., Mooncake: A KVCache-centric Disaggregated Architecture for LLM Serving(https://arxiv.org/abs/2407.00079)
- Yao et al., TaiChi(动态实例角色切换)(https://arxiv.org/abs/2405.18303)
- vLLM 官方文档:Disaggregated Prefilling(https://docs.vllm.ai/en/latest/features/disagg_prefill.html)