📦 GitHub: https://github.com/geekchow/deepseek-v41-flash-inference-guide
背景:本文选自开源学习指南《DeepSeek V4.1 Flash 大规模推理部署实践》。全书用一个贯穿示例串联:编程 Agent 通过 Messages 协议发起第三轮请求,上下文 10 万 token、带一张截图、以
edit_file工具调用结束;文中的“第 N 步”即指该示例中的步骤,完整场景见 04 贯穿示例。
你在哪里:第 5 章第 3 篇,贯穿示例的第 ③(Prefill 分配)、第 ④(截图分配)、第 ⑤ 中的专家均衡、第 ⑥(Decode 分配)。
读完本文你将知道:四个均衡器(Prefill LB、Decode LB、VE LB、EPLB)各自均衡什么、用什么信号、参数是多少,以及整体能达到理想均衡的多少。
开源状态:DP 副本间的 Prefill / Decode / VE 负载均衡未开源;EPLB 包含在 DeepInfer 中(README:“MoE EP + EPLB”)。
1. 角色回顾
- 负责:把请求分配到 DP(Data Parallelism,数据并行)副本,把专家分配到 GPU,使最慢的那张卡不拖累整组。
- 为什么需要四个:注意力按 DP 切(#DP = #EP),每个 DP rank 处理不同请求 → 请求级不均衡;MoE 按 EP 切,热门专家所在的卡更忙 → 专家级不均衡;图片编码独立部署 → 需要单独均衡。
- 出现在示例:③④⑤⑥。
下图回答:四个均衡器分别作用在哪一层。
2. 内部设计
2.1 核心度量:计算配额(compute quota)
请求长短悬殊(几百 token 到几十万 token),“每台机器分几个请求”毫无意义。Prefill 侧把每一步的工作量统一换算成时间(纳秒):
- 用离线拟合的二次代价模型
cost(q, c)预测一步耗时,q= 本步要算的 token 数,c= 该序列已算好的前缀长度。 - 为什么是二次:注意力部分大致正比于
q × (c + q/2),线性层和 MoE 部分正比于q。(这是帮助理解的示意形式,官方未公开具体系数。)
2.2 Prefill LB:四级分配
下图回答:一个 prefill 请求从集群级到单卡级经历哪四次决策。
- 分档(tiers):按总上下文长度分 4 档:
<16K / 16–64K / 64–256K / ≥256K。每档分到的引擎数按上一轮该档的计算配额需求占比动态重分配;每档有自己的单批配额上限。分档的意义:防止一个 50 万 token 的请求和一堆短请求挤在同一批,拖垮短请求的 TTFT。 - 跨 DP 组:引擎按自己剩余的计算配额主动拉取(pull,而不是 push)。由于任意引擎都能命中任意前缀(Block KV 在共享 3FS),没有前缀亲和路由。
- DP 组内:一批请求交给一个引擎,每个请求到达时分给当前配额负载最低的 DP rank。
- 单卡内:请求 FIFO(First In, First Out,先进先出)出队,chunk 大小不固定,而是按预算求解:每个 prefill 步给定时间预算
compute_quota_limit(纳秒),解二次方程cost(q, c) = 剩余预算得到最大的q,同时不超过32,768 token上限。预算本身再在 DP 组内做all-reduce MIN——组内各 rank 取最小值,保证 EP 组内各卡每步耗时一致(MoE 层要同步,最慢的卡决定整组速度)。
2.3 Decode LB:同时均衡两个维度
Decode LB 在所有 decode 实例的所有 GPU之间统一路由,同时均衡:
| 维度 | 决定什么 |
|---|---|
| 请求数(并发) | 每步的批大小 → MoE / 线性层的耗时 |
| KV 总长度 | 注意力部分的耗时 |
启发式规则:短请求发往“并发低但 KV 总长大”的卡;长请求发往“KV 总长小”的卡。这样两个维度互相填空。细节可参考 DualPath 的组内调度算法。
2.4 VE LB:按图片 patch 数均衡
- 以机器为粒度:prefill 机器随机挑一台“每卡一个视觉编码器”的 VE 机器;
- 在该机器上,交给当前正在处理的图像 patch 最少的 GPU;
- patch 数直接由尺寸算出:
(width / patch_size) × (height / patch_size); - VE 单卡一个实例即可,推荐用更便宜的卡。
2.5 EPLB:专家级均衡
机制(prefill 与 decode 相同):
- 每张卡除了自己负责的专家外,再放几个冗余专家,用来托管热门专家的副本;
- 统计最近 N 步每个专家的负载,每 M 步按统计重新把专家分配到各卡;
- 专家权重通过 NVLink
symm_mem在组内搬移。
参数与实测(moe_lb= 最慢 rank / 平均值,1.0 为完美):
| 每卡起始冗余专家 | 统计窗口 N | 重排周期 M | 实测moe_lb | |
|---|---|---|---|---|
| Prefill | 4(向上取整使总专家数为ep_size的倍数) | 100 步 | 每 1,000 步 | 1.074(EP4DP4)/ 1.045(EP2DP2) |
| Decode | 1(同样取整) | 1,000 步 | 每 10,000 步 | 1.090 |
Decode 的例子:EP64 时 384 个路由专家 + 64 个冗余 = 448 个,每卡7 个(不含共享专家)。官方基准图中 EP64DP64 标注为 “384+64 experts”,EP8DP8 为 “384+32”,EP4DP4 prefill 为 “384+32”。
为什么 decode 的重排更稀疏:decode 每步只处理少量 token,单步统计噪声大,需要更长窗口;而且 EP64 跨 64 卡搬权重的代价更高。
2.6 整体效果
与离线基准(理想均衡)相比,线上大约达到80%。剩下 20% 来自请求到达的随机性、长短混合、专家热度漂移等。
3. 交互契约
| 均衡器 | 输入信号 | 输出决策 | 不做什么 |
|---|---|---|---|
| Prefill LB | 总上下文长度、命中长度、各引擎剩余配额 | 档位 → 引擎 → DP rank → chunk 大小 | 不考虑前缀亲和 |
| Decode LB | 各卡并发数、各卡 KV 总长、请求的 KV 长度 | 目标 decode GPU | 不按“请求数”单维度均衡 |
| VE LB | 图片尺寸、各卡在处理的 patch 数 | VE 机器 + GPU | 不参与文本 prefill |
| EPLB | 最近 N 步每专家负载 | 专家 → GPU 映射 | 不改变路由结果(模型语义不变) |
4. ⚓ 回到示例
第 ③ 步:Prefill 分配
- 总上下文 100,000 → 落入64–256K 档。
- 需要计算的 token:3,104 个未命中 token(若发生变体回退则为 3,232),已算前缀
c = 96,896。配额估算cost(3104, 96896):注意力部分≈ 3,104 × (96,896 + 1,552),是长前缀主导的“短 q、长 c”型请求——在同档中它的配额远大于一个 3 千 token 的新会话,均衡器正是靠这个区分。 - 64–256K 档里某个剩余配额最多的引擎拉走它;组内分给配额负载最低的 rank。
- 单卡内:3,104 < 32,768,且时间预算足够 →一个 chunk 算完。若该 rank 本步预算已被其他请求占去大半,则二次方程解出更小的
q,请求被切成两步。
第 ④ 步:截图分配
截图 1280 × 800,按假设的patch_size = 16算:(1280/16) × (800/16) = 80 × 50 = 4,000个 patch。prefill 机器随机挑一台 VE 机器,交给当前 patch 最少的那张卡。
第 ⑤ 步中的 EPLB
prefill 组为 EP4DP4:4 张卡,384 个路由专家 + 冗余。我们这 3,104 个 token 路由到的热门专家(比如处理代码 token 的专家)可能已有副本分布在多张卡上,使最慢卡与平均值的比约 1.07。
第 ⑥ 步:Decode 分配
我们的请求 KV 长达约 10 万 token → 按“长请求去 KV 总长小的卡”规则,Decode LB 选一张当前 KV 总长最小的卡(即使它并发数不是最低)。这样不会让一张已经背着多个长上下文的卡的注意力耗时继续膨胀。
5. 故障行为
| 情况 | 应对 |
|---|---|
| 某档流量突增(例如大量 256K+ Agent 请求) | 下一轮按配额需求占比重新分配各档引擎数 |
| 代价模型预测偏差 | 预算在 DP 组内 all-reduce MIN,偏差不会让某个 rank 单独超时太多 |
| 专家热度漂移 | 周期性重排(prefill 每 1,000 步、decode 每 10,000 步) |
| decode 卡被长请求堆满 | 两维均衡启发式把后续长请求引走;必要时扩容(见 弹性与容错) |
上一篇:05.2 Entrypoint 与 KV 存储 | 下一篇:05.4 DeepInfer 总览 | 返回:概念地图
📚 返回专栏目录