📦 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 切,热门专家所在的卡更忙 → 专家级不均衡;图片编码独立部署 → 需要单独均衡。
- 出现在示例:③④⑤⑥。
下图回答:四个均衡器分别作用在哪一层。
#mermaid-svg-OneOBkWWmDfIgw9X{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OneOBkWWmDfIgw9X .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OneOBkWWmDfIgw9X .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OneOBkWWmDfIgw9X .error-icon{fill:#552222;}#mermaid-svg-OneOBkWWmDfIgw9X .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OneOBkWWmDfIgw9X .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OneOBkWWmDfIgw9X .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OneOBkWWmDfIgw9X .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OneOBkWWmDfIgw9X .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OneOBkWWmDfIgw9X .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OneOBkWWmDfIgw9X .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OneOBkWWmDfIgw9X .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OneOBkWWmDfIgw9X .marker.cross{stroke:#333333;}#mermaid-svg-OneOBkWWmDfIgw9X svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OneOBkWWmDfIgw9X p{margin:0;}#mermaid-svg-OneOBkWWmDfIgw9X .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OneOBkWWmDfIgw9X .cluster-label text{fill:#333;}#mermaid-svg-OneOBkWWmDfIgw9X .cluster-label span{color:#333;}#mermaid-svg-OneOBkWWmDfIgw9X .cluster-label span p{background-color:transparent;}#mermaid-svg-OneOBkWWmDfIgw9X .label text,#mermaid-svg-OneOBkWWmDfIgw9X span{fill:#333;color:#333;}#mermaid-svg-OneOBkWWmDfIgw9X .node rect,#mermaid-svg-OneOBkWWmDfIgw9X .node circle,#mermaid-svg-OneOBkWWmDfIgw9X .node ellipse,#mermaid-svg-OneOBkWWmDfIgw9X .node polygon,#mermaid-svg-OneOBkWWmDfIgw9X .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OneOBkWWmDfIgw9X .rough-node .label text,#mermaid-svg-OneOBkWWmDfIgw9X .node .label text,#mermaid-svg-OneOBkWWmDfIgw9X .image-shape .label,#mermaid-svg-OneOBkWWmDfIgw9X .icon-shape .label{text-anchor:middle;}#mermaid-svg-OneOBkWWmDfIgw9X .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OneOBkWWmDfIgw9X .rough-node .label,#mermaid-svg-OneOBkWWmDfIgw9X .node .label,#mermaid-svg-OneOBkWWmDfIgw9X .image-shape .label,#mermaid-svg-OneOBkWWmDfIgw9X .icon-shape .label{text-align:center;}#mermaid-svg-OneOBkWWmDfIgw9X .node.clickable{cursor:pointer;}#mermaid-svg-OneOBkWWmDfIgw9X .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OneOBkWWmDfIgw9X .arrowheadPath{fill:#333333;}#mermaid-svg-OneOBkWWmDfIgw9X .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OneOBkWWmDfIgw9X .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OneOBkWWmDfIgw9X .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OneOBkWWmDfIgw9X .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OneOBkWWmDfIgw9X .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OneOBkWWmDfIgw9X .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OneOBkWWmDfIgw9X .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OneOBkWWmDfIgw9X .cluster text{fill:#333;}#mermaid-svg-OneOBkWWmDfIgw9X .cluster span{color:#333;}#mermaid-svg-OneOBkWWmDfIgw9X div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OneOBkWWmDfIgw9X .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OneOBkWWmDfIgw9X rect.text{fill:none;stroke-width:0;}#mermaid-svg-OneOBkWWmDfIgw9X .icon-shape,#mermaid-svg-OneOBkWWmDfIgw9X .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OneOBkWWmDfIgw9X .icon-shape p,#mermaid-svg-OneOBkWWmDfIgw9X .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OneOBkWWmDfIgw9X .icon-shape .label rect,#mermaid-svg-OneOBkWWmDfIgw9X .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OneOBkWWmDfIgw9X .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OneOBkWWmDfIgw9X .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OneOBkWWmDfIgw9X :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 图片
Entrypoint
Prefill LB
按上下文长度分档
按计算配额拉取
某个 DP 组内
选配额最低的 rank
EPLB
组内专家重排
VE LB
选 patch 最少的卡
Decode LB
均衡并发数
与 KV 总长
EPLB
EP64 专家重排
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 请求从集群级到单卡级经历哪四次决策。
#mermaid-svg-Hj85ZmwEFwVm4ygD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Hj85ZmwEFwVm4ygD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Hj85ZmwEFwVm4ygD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Hj85ZmwEFwVm4ygD .error-icon{fill:#552222;}#mermaid-svg-Hj85ZmwEFwVm4ygD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Hj85ZmwEFwVm4ygD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Hj85ZmwEFwVm4ygD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Hj85ZmwEFwVm4ygD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Hj85ZmwEFwVm4ygD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Hj85ZmwEFwVm4ygD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Hj85ZmwEFwVm4ygD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Hj85ZmwEFwVm4ygD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Hj85ZmwEFwVm4ygD .marker.cross{stroke:#333333;}#mermaid-svg-Hj85ZmwEFwVm4ygD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Hj85ZmwEFwVm4ygD p{margin:0;}#mermaid-svg-Hj85ZmwEFwVm4ygD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Hj85ZmwEFwVm4ygD .cluster-label text{fill:#333;}#mermaid-svg-Hj85ZmwEFwVm4ygD .cluster-label span{color:#333;}#mermaid-svg-Hj85ZmwEFwVm4ygD .cluster-label span p{background-color:transparent;}#mermaid-svg-Hj85ZmwEFwVm4ygD .label text,#mermaid-svg-Hj85ZmwEFwVm4ygD span{fill:#333;color:#333;}#mermaid-svg-Hj85ZmwEFwVm4ygD .node rect,#mermaid-svg-Hj85ZmwEFwVm4ygD .node circle,#mermaid-svg-Hj85ZmwEFwVm4ygD .node ellipse,#mermaid-svg-Hj85ZmwEFwVm4ygD .node polygon,#mermaid-svg-Hj85ZmwEFwVm4ygD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Hj85ZmwEFwVm4ygD .rough-node .label text,#mermaid-svg-Hj85ZmwEFwVm4ygD .node .label text,#mermaid-svg-Hj85ZmwEFwVm4ygD .image-shape .label,#mermaid-svg-Hj85ZmwEFwVm4ygD .icon-shape .label{text-anchor:middle;}#mermaid-svg-Hj85ZmwEFwVm4ygD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Hj85ZmwEFwVm4ygD .rough-node .label,#mermaid-svg-Hj85ZmwEFwVm4ygD .node .label,#mermaid-svg-Hj85ZmwEFwVm4ygD .image-shape .label,#mermaid-svg-Hj85ZmwEFwVm4ygD .icon-shape .label{text-align:center;}#mermaid-svg-Hj85ZmwEFwVm4ygD .node.clickable{cursor:pointer;}#mermaid-svg-Hj85ZmwEFwVm4ygD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Hj85ZmwEFwVm4ygD .arrowheadPath{fill:#333333;}#mermaid-svg-Hj85ZmwEFwVm4ygD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Hj85ZmwEFwVm4ygD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Hj85ZmwEFwVm4ygD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Hj85ZmwEFwVm4ygD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Hj85ZmwEFwVm4ygD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Hj85ZmwEFwVm4ygD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Hj85ZmwEFwVm4ygD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Hj85ZmwEFwVm4ygD .cluster text{fill:#333;}#mermaid-svg-Hj85ZmwEFwVm4ygD .cluster span{color:#333;}#mermaid-svg-Hj85ZmwEFwVm4ygD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Hj85ZmwEFwVm4ygD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Hj85ZmwEFwVm4ygD rect.text{fill:none;stroke-width:0;}#mermaid-svg-Hj85ZmwEFwVm4ygD .icon-shape,#mermaid-svg-Hj85ZmwEFwVm4ygD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Hj85ZmwEFwVm4ygD .icon-shape p,#mermaid-svg-Hj85ZmwEFwVm4ygD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Hj85ZmwEFwVm4ygD .icon-shape .label rect,#mermaid-svg-Hj85ZmwEFwVm4ygD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Hj85ZmwEFwVm4ygD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Hj85ZmwEFwVm4ygD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Hj85ZmwEFwVm4ygD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 请求 + 命中长度
按总上下文长度分档
小于 16K
16--64K
64--256K
256K 及以上
跨 DP 组
引擎按剩余配额
主动拉取
DP 组内
分给配额负载
最低的 rank
单卡内 FIFO
按时间预算
求解 chunk 大小
- 分档(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 总览 | 返回:概念地图
📚 返回专栏目录