概述

DeepSeek V3 的 MoE(Mixture of Experts)模块是其核心组成部分,采用 Shared + Routed Expert 架构。本文基于 vLLM 代码库,深入分析其计算流程、通信模式、量化方案以及性能优化策略。

整体架构

DeepSeek V3 MoE 层的代码入口为 DeepseekV2MoE,整体结构如下:

DeepseekV2MoE
├── GateLinear          → 路由 logits 生成
├── SharedExperts       → 所有 token 共享的 dense MLP
└── FusedMoE            → 稀疏 routed expert 计算
    ├── ExpertMapManager → 专家映射(global → local)
    ├── Router (GroupedTopK) → top-k 专家选择
    └── MoERunner       → 执行引擎(dispatch → 计算 → combine)

核心设计思想:所有 token 先经过一个共享的 dense MLP,再经路由选择少量专家计算,两者结果相加

shared_out = shared_experts(hidden_states)   # 所有 token 都计算
final = shared_out + routed_out               # 两者相加

Gating 与 Routing

GateLinear

GateLinear 将 hidden_states 映射到 n_routed_experts 维的 logits 向量,采用三级 GEMM 调度:

  1. DSV3 专用 kernel(SM90+, batch ≤ 16)— 最低延迟
  2. cuBLAS bf16 → fp32(SM90+, bf16 权重)
  3. 标准 F.linear 兜底

topk_method == "noaux_tc",gate 额外携带 e_score_correction_bias(per-expert 可学习偏置),用于无辅助 loss 的负载均衡。

Grouped Top-k Routing

DeepSeek V3 采用独有的两级路由策略,先选 group 再选 expert:

router_logits → sigmoid/softmax → scores
  → 每 group 取 top-2 scores 之和 → 选 topk_group 个 group
  → mask 掉未选中 group → 从剩余 expert 中选 top_k 个
  → renormalize → * routed_scaling_factor
  → topk_weights, topk_ids

关键设计:若 e_score_correction_bias 存在,selection 用 biased scores(实现负载均衡),weights 用 original unbiased scores(保证精度),两者分离。

路由计算结果被后续 expert 计算和通信层使用:

  • topk_ids:每个 token 选中的 expert 编号
  • topk_weights:每个选中 expert 的权重(用于 combine 阶段的加权求和)

通信模式

DeepSeek V3 MoE 的通信发生在三个维度:TP(Tensor Parallel)、DP/EP(Expert Parallel)、PCP(Pipeline Cross-Parallel)。

通信流水线

Forward Input (全量 token)
    │
    ▼
Sequence Parallel Chunk (if is_sequence_parallel)
    │
    ▼
Dispatch (AllGather / All2All)
    │ token 按 expert 路由分散到对应 EP rank
    ▼
Expert Compute (各 EP rank 计算本地 expert)
    │
    ▼
Combine (ReduceScatter / All2All)
    │ 结果归集回各 rank
    ▼
All-Reduce (final, 若 combine 未做 reduction)
    │
    ▼
Sequence Parallel AllGather (恢复完整序列)
    │
    ▼
Final Output

通信后端

vLLM 提供了丰富的 all2all 通信后端,在 all2all_utils.py 中统一管理:

后端适用场景特点
DeepEP High-throughput大 batch高吞吐,异步 dispatch/combine
DeepEP Low-latency小 batch支持 FP8 dispatch,通信量减半
FlashInfer NVLink Two-sidedNVLink双向 NVLink 通信
FlashInfer NVLink One-sidedNVLink支持 nvfp4/mxfp8 dispatch
AllGather + ReduceScatter通用兜底朴素 AG+RS,无需特殊库
NIXL EPRDMA 跨节点支持 FP8 dispatch
MORIROCmAMD AITER 后端

模块化内核(Modular Kernel)框架

vLLM 的 MoE 采用 Modular Kernel (MK) 框架,核心目的是解耦通信和计算,避免通信后端 × 计算后端的组合爆炸。

设计思想

传统方案的问题:若有 M 个计算后端 × N 个通信后端,需要 M×N 个组合实现。

模块化解法:

FusedMoEKernel
├── prepare_finalize: 通信层
│     prepare():  量化 + dispatch (all2all/ag)
│     finalize(): 权重应用 + reduce (all2all/rs)
└── fused_experts: 计算层
      w13 → activation → w2 (matmul)

只需要 M + N 个实现即可,通过 FusedMoEKernel 编排流水线 prepare → compute → finalize

核心类层次

通信层(Prepare/Finalize)

  • FusedMoEPrepareAndFinalizeModular — 接收路由后结果(topk_weights, topk_ids),做量化 + all2all dispatch 和 combine
  • FusedMoEPrepareAndFinalizeMonolithic — 接收原始 logits,内部 fused 路由

计算层(Experts)

  • FusedMoEExpertsModular — 执行 w13 → act → w2,不关心通信,只处理已 dispatch 到本地的 token
  • FusedMoEExpertsMonolithic — 内部自己做 routing + compute

编排层(Kernel)

  • FusedMoEKernel — 对外统一接口,根据 impl 类型 dispatch
  • FusedMoEKernelModularImpl — 编排流水线 _prepare → _fused_experts → _finalize

Oracle 模式

Oracle 是智能后端选择器 + 内核工厂,在 oracle/ 目录下,每种量化类型对应一个文件:

oracle/
├── fp8.py          → select_fp8_moe_backend() + make_fp8_moe_kernel()
├── int8.py         → select_int8_moe_backend()
├── nvfp4.py        → select_nvfp4_moe_backend()
├── mxfp4.py        → select_mxfp4_moe_backend()
└── unquantized.py  → select_unquantized_moe_backend()

两阶段决策:

  1. Phase 1select_*_backend() — 按优先级检查各后端是否支持当前配置(SM 版本、batch 大小、EP/DP 配置等),返回最优可用后端
  2. Phase 2make_*_kernel() — 组合 prepare_finalize(通信层)+ experts(计算层)为完整的 FusedMoEKernel

两条路径对比

MoE 的计算与通信有两个独立且互斥的路径,通过 do_naive_dispatch_combine 切换:

do_naive_dispatch_combine = dp_size > 1 and not supports_internal_mk
Path 1: _maybe_dispatch + _maybe_combinePath 2: prepare_finalize (MK 内部)
触发条件DP>1 且 kernel 不支持 MKkernel 是 MK 框架
位置_forward_impl 中 quant 方法前后FusedMoEKernelModularImpl 内部
操作对象路由前的 hidden_states + router_logits路由后的 hidden_states + topk_weights + topk_ids
通信方式AllGather / ReduceScatterDeepEP / FlashInfer / NIXL / AgRs
处置维度DP(复刻 token)EP(按 topk_ids 分发)

执行流程

_forward_impl 中的完整执行流

_forward_impl:
  _maybe_dispatch(hidden_states, router_logits)
    ├── [Path 1] DP>1 && !supports_internal_mk → all_gatherv
    └── [Path 2] 否则 → no-op

  _apply_quant_method(hidden_states, router_logits)
    ├── routing: select_experts() → topk_weights, topk_ids
    └── apply(x, topk_weights, topk_ids)
          └── [Path 2] MK 内核:
                prepare_finalize.prepare()   # all2all dispatch
                fused_experts.compute()      # 专家前向
                prepare_finalize.finalize()  # all2all combine

  _maybe_combine(shared_out, hidden_states)
    ├── [Path 1] → reduce_scatterv()
    └── [Path 2] → no-op

完整调用链

_apply_quant_method()                         # moe_runner.py
  └── quant_method.apply(x, topk_weights, topk_ids, ...)
        └── self.moe_kernel.apply(x, w1, w2, ...)  # modular_kernel.py
              └── FusedMoEKernelModularImpl.apply()
                    ├── _prepare()
                    │     └── prepare_finalize.prepare() / prepare_async()
                    ├── _fused_experts()     # w13 → act → w2
                    └── _finalize()
                          └── prepare_finalize.finalize() / finalize_async()

异步机制

MoE 的 async 机制利用 dispatch all2all 通信时间去做 shared expert 计算,通过 hook + receiver 解耦通信发起和结果等待。

单次 forward 内的 stream overlap

prepare_async():
  1. 切到通信流
  2. buffer.dispatch(x, topk_ids, ...)  ← 发起 all2all (不阻塞)
  3. 记录 handle
  4. 切回计算流
  5. return lambda: _receiver(event, ...)

_receiver():
  1. event.current_stream_wait()         ← 等 dispatch 完成
  2. 修正 topk_ids (local→global)
  3. return (a1q, a1q_scale, ...)

调用方先做 shared expert,需要 dispatch 结果时再调 receiver() 阻塞。

DBO(Dynamic Batching Overlap)

DBO 在 async 的基础上增加 micro-batch 粒度的调度,让通信和计算在更细粒度上重叠:

无 DBO: prepare_async → hook(shared expert) → receiver(wait) → compute → finalize

有 DBO: ubatch 0 prepare → dbo_yield → ubatch 1 compute ←→ ubatch 0 receiver
         ↑ ubatch 0 的 dispatch 和 ubatch 1 的计算被交错

async_finish = self.async_prepare and not dbo_enabled() 控制 DeepEP 内部是否自主管理同步。DBO 启用时,由 DBO 调度器接管同步控制。

量化方案

DeepSeek V3 FP8

DeepSeek V3 使用 FP8 block 量化(E4M3 数据,UE8M0 scale):

{
  "quant_method": "fp8",
  "fmt": "e4m3",
  "scale_fmt": "ue8m0",
  "weight_block_size": [128, 128]
}

权重量化是真正的 2D block quant:输出通道每 128 一组 × 输入通道每 128 一组。

激活(activation)量化在 prepare 阶段进行,使用 per-token per-group 方式(沿 hidden dim 每 128 一组),而非 2D block。这是因为 activation 按 token 独立量化,block_shape 的 [128, 128] 只用到第二个维度作为 group_size。

FP8 Dispatch

DeepEP Low-latency 后端支持 FP8 dispatch:在通信阶段将 BF16 数据量化为 FP8,传输后再恢复,通信量减半

无量化: a1: BF16 → all2all BF16              → 2 bytes/element
FP8:    a1: BF16 → quant FP8 → all2all FP8    → 1 byte/element

量化时机决策树:

prepare 中是否做 input quantization?
  ├─ FP8 block quant → dispatch 前量化(DeepEP 支持)
  └─ 其他情况 → dispatch 后量化(DeepEP 不支持 per-tensor FP8 dispatch)

这与精度无关(量化是逐元素的),仅取决于 DeepEP 对 dispatch 数据格式的支持能力:DeepEP 只支持 block FP8 格式做 FP8 dispatch,不支持 per-tensor/per-channel。

DeepSeek V4 MXFP4(可选)

DeepSeek V4 额外支持 MXFP4(W4A8 OCP MX 标准):

组件方案细节
权重MXFP4(静态)uint8 打包两个 FP4,block_size=32
激活MXFP8(动态)per-tensor 动态 scale
兜底FP8 或 BF16取决于后端支持

专家映射与负载均衡

ExpertMapManager

管理 global expert ID ↔ physical/local expert ID 的映射。包含三个路由表:

  • global_to_physical:全局 expert ID → 物理 expert ID(所有 EP rank 拼接后的绝对位置)
  • physical_to_global:物理 expert ID → 全局 expert ID(反向查找)
  • local_global:local index → 全局 expert ID(当前 rank 的本 expert 编号)

放置策略

Linear(默认)

rank0: experts 0,1 | rank1: experts 2,3 | rank2: experts 4,5

映射关系 trivial,routing_tables = None

Round-robin

rank0: experts 0,4 | rank1: experts 1,5 | rank2: experts 2,6

需要 routing_tables 查表定位,用于 DeepEP LL 等后端的发送端寻址。

EPLB(弹性负载均衡)

通过冗余 expert 副本 + Triton kernel 动态负载重分布,消除 EP 热点,吞吐提升 20-30%。

性能优化汇总

优化点收益适用场景
FP8 量化 (128×128 block)计算吞吐 +2x, 显存 -2xSM90+
FP8 Dispatch通信带宽 -2xDeepEP LL / NIXL
DeepEP all2all比 AG+RS 延迟低 3-5xNVLink 互联
Async prepare/finalize通信完全 overlapDeepEP / FlashInfer
Grouped topk路由计算量 -50%+DeepSeek V3 特有
EPLB消除 EP 热点,吞吐 +20-30%负载不均衡场景
Workspace 复用显存节省 30%+所有场景
Weight 重排kernel 吞吐 +15-30%Cutlass/DeepGemm
Sequence ParallelMoE 计算量 / tp_sizeTP > 1

总结

DeepSeek V3 的 MoE 模块在 vLLM 中的实现体现了高度工程化的设计:

  1. 分组路由:两级 top-k 选择,配合 e_score_correction_bias 实现无辅助 loss 的负载均衡
  2. 通信-计算解耦:Modular Kernel 框架将通信和计算拆分为可独立替换的策略,避免组合爆炸
  3. 多层次并行:TP/EP/DP/SP/PCP 多维度并行,配合 async prepare/finalize 和 DBO 实现通信-计算重叠
  4. 极致量化:FP8 block quant 配合 FP8 dispatch,减少通信量一倍
  5. Oracle 智能选择:自动选择当前硬件和配置下的最优后端组合