为什么 MoE 推理会慢在”最热专家”
Mixture-of-Experts(MoE)的卖点是稀疏激活:模型拥有很多专家,但每个 Token 通常只选择少数几个专家执行。这样可以扩大总参数规模,而不让每个 Token 都执行全部参数。
但稀疏激活并不等于分布式推理天然高效。生产系统中,MoE 层通常要经历四步:
- 路由器为每个 Token 选择 Top-K 专家。
- 将 Token 激活值发送到承载目标专家的 GPU。
- 各 GPU 执行本地专家计算。
- 将专家输出发回原始并行分片并合并。
当专家分散在多张 GPU 或多个节点上时,第 2、4 步通常依赖 All-to-All 通信。整个 MoE 层还存在同步边界:某些 GPU 已经处理完本地专家,也可能必须等待最繁忙的 GPU。
因此,MoE 服务的尾延迟往往不是由”平均专家负载”决定,而是由最热专家、最慢链路和最拥塞设备共同决定。
核心原理:Expert Parallel 到底在并行什么
Expert Parallel 不是 Tensor Parallel 的同义词
Tensor Parallel(TP) 把同一层的矩阵计算切分到多张 GPU,每次前向都需要这些 GPU 协作完成同一组权重计算。
Expert Parallel(EP) 把不同专家放到不同 GPU。Token 由路由器选择专家后,激活值跨设备移动到目标专家。它减少了单卡需要常驻的专家权重,但引入了动态路由和 All-to-All 通信。
在大型 MoE 部署中,EP 通常还会与 TP、Data Parallel(DP)组合。问题也随之变成多维的:
- TP 决定单个专家或共享层如何切分。
- EP 决定专家放在哪些设备。
- DP 决定整套模型副本如何承接请求。
- 网络拓扑决定跨卡和跨节点路由成本。
负载均衡有三个层次
生产中不要只观察”每个专家累计处理了多少 Token”,至少要区分三个层次:
- 专家级负载:某个专家是否长期过热。
- 设备级负载:多个热门专家是否恰好集中在同一 GPU。
- 通信级负载:Token 是否频繁跨节点访问专家,造成链路拥塞。
即使所有专家的累计 Token 数接近,也可能因为放置不合理,让一张 GPU 同时承载多个高峰相关专家;或者让经常共同激活的专家分布在不同节点,放大网络流量。
训练时均衡不代表线上均衡
训练阶段常用辅助损失、容量因子或路由偏置来避免专家塌缩,但线上流量会发生域偏移。代码、数学、中文客服、长文摘要等请求可能激活不同专家组合。
生产负载还具有明显的时间结构:
| 维度 | 特征 |
|---|---|
| 昼夜差异 | 白天与夜间业务不同 |
| 批量离线 | 大批量离线任务会改变专家分布 |
| Prefill 阶段 | 一次进入大量 Token |
| Decode 阶段 | 每步 Token 少,但同步次数多 |
| 租户偏差 | 某个租户或提示词模板可能持续命中特定专家 |
所以,Expert Parallel 的治理对象不是静态模型结构,而是模型结构、流量分布和硬件拓扑的联合状态。
工程落地:从专家画像到动态副本
第一步:建立专家负载画像
建议按模型版本、MoE 层、专家、阶段和流量类型记录以下指标:
expert_tokens_total:专家接收的 Token 数。expert_load_max_mean_ratio:窗口内最大专家负载与平均值之比。expert_load_cv:专家负载变异系数。expert_compute_time_ms:每个专家的计算时间。all_to_all_bytes与all_to_all_time_ms:通信量和通信耗时。cross_node_token_ratio:跨节点路由 Token 占比。expert_queue_depth:专家或设备侧等待队列。moe_layer_p95_ms:各 MoE 层 P95 延迟。replica_migration_count:专家副本迁移次数。dropped_or_overflow_tokens:容量溢出或丢弃 Token 数。
指标必须按 Prefill/Decode 分开,否则一次长 Prompt 的高峰可能掩盖 Decode 阶段持续发生的同步抖动。
可以使用以下通用逻辑判断是否出现持续热点:
from dataclasses import dataclass
@dataclass
class ExpertWindow:
max_tokens: int
mean_tokens: float
p95_layer_ms: float
all_to_all_ms: float
windows_over_threshold: int
def should_rebalance(
window: ExpertWindow,
max_mean_threshold: float = 1.8,
min_consecutive_windows: int = 3,
) -> bool:
if window.mean_tokens <= 0:
return False
load_ratio = window.max_tokens / window.mean_tokens
return (
load_ratio >= max_mean_threshold
and window.windows_over_threshold >= min_consecutive_windows
and window.all_to_all_ms > 0
)
这段代码只表达治理逻辑。实际阈值应通过真实流量回放和压测确定,不应直接照抄固定数值。
第二步:把专家放置当成拓扑问题
专家放置不能只做平均分配。至少要考虑:
- GPU 是否位于同一 NVLink/NVSwitch 域。
- 跨节点链路带宽和拥塞情况。
- 哪些专家经常被同一类请求共同激活。
- 哪些专家在 Prefill 与 Decode 阶段热度不同。
- 某张 GPU 上共享层、KV Cache 和专家权重的显存竞争。
一个实用策略是先做静态拓扑感知放置:
- 将最热专家分散到不同设备。
- 将经常共同激活的专家尽量放在低成本互联域。
- 避免多个高方差专家集中在同一 GPU。
- 为动态副本保留显存水位,而不是把 HBM 在启动时占满。
第三步:为热点专家建立选择性副本
当一个专家持续过热时,可以在其他 GPU 上创建副本,让路由器在等价副本间选择负载更低或网络更近的一份。
副本机制需要四个约束:
- 热度门槛:必须连续多个窗口超过阈值。
- 收益门槛:预计减少的排队和通信时间要高于复制成本。
- 显存门槛:副本不能挤压 KV Cache 或触发 OOM。
- 稳定门槛:使用冷却时间和滞回区间,避免副本反复创建、删除。
通用配置可以表达为:
moe_serving:
profile_window_seconds: 60
hotspot_detection:
max_to_mean_ratio: 1.8
consecutive_windows: 3
cooldown_seconds: 600
expert_replica:
enabled: true
max_extra_replicas_per_expert: 2
min_free_gpu_memory_ratio: 0.15
migration_budget_per_hour: 4
topology:
prefer_intra_node: true
cross_node_token_ratio_alert: 0.25
rollback:
max_p95_regression_ratio: 0.10
max_error_rate_regression_ratio: 0.02
这同样是框架无关的示例,不对应某个推理引擎的真实参数名。
第四步:为 All-to-All 建立明确预算
All-to-All 不应被当作”框架内部细节”。它需要独立 SLO:
- 单层 All-to-All 的 P50/P95/P99 耗时。
- All-to-All 占单步 Decode 时间的比例。
- 单请求跨节点字节数。
- 通信与专家计算的重叠比例。
- 网络拥塞时的降级行为。
如果通信时间持续上升,应优先判断是哪一种问题:
- 专家放置导致远程访问过多。
- 某些专家热点使接收端形成拥塞。
- 批次过小,通信固定开销占比过高。
- 批次过大,单次交换的数据量和尾部等待过高。
- 网络拓扑或 NCCL 配置未被正确利用。
- 多个模型或租户共享链路造成干扰。
第五步:不要用”丢 Token”掩盖容量问题
部分 MoE 训练设计允许容量溢出后丢弃 Token,但生产推理不能默认认为 Token 丢弃是安全降级。除非模型架构、训练方式和推理实现明确支持,否则它可能改变输出质量且难以定位。
更安全的治理顺序通常是:
- 减小单批 Token 上限。
- 对长 Prefill 做独立限流。
- 降低并发或执行 Admission Control。
- 调整专家放置或创建副本。
- 扩展 EP/DP 资源。
- 在可验证前提下启用框架提供的专用溢出策略。
第六步:区分 Prefill 和 Decode 的治理策略
Prefill 的 Token 数多,容易制造短时专家洪峰;Decode 每步 Token 数少,但要重复执行大量同步步骤。因此可以分别治理:
Prefill:
- 按 Prompt 长度分桶。
- 限制超长请求同时进入同一 EP 组。
- 将批量离线流量与在线流量隔离。
- 关注瞬时专家峰值和单次 All-to-All 数据量。
Decode:
- 关注每步尾延迟和最慢专家。
- 避免批次中混入极端慢请求。
- 关注热点持续时间,而不是单次峰值。
- 优先减少跨节点路由与同步等待。
适用场景
这套方法适合:
- 部署 Mixtral、DeepSeek、Qwen MoE 等稀疏专家模型。
- 单机无法容纳全部专家,需要跨 GPU 或跨节点推理。
- 平均吞吐尚可,但 P95/P99 延迟不稳定。
- 不同业务域导致专家激活分布明显不同。
- GPU 利用率看似不高,却出现部分设备排队和通信拥塞。
- 模型升级后专家热点位置发生变化。
对于参数规模较小、单机即可容纳、网络通信不是瓶颈的 MoE 模型,复杂的动态副本系统未必值得。先用静态放置和完整指标确认问题,再引入在线重排。
常见误区
误区一:MoE 每个 Token 只算少数专家,所以一定比 Dense 模型快
MoE 减少的是激活计算,不会自动消除权重驻留、Token 分发、All-to-All 和同步等待。硬件拓扑不匹配时,通信成本可能抵消稀疏计算收益。
误区二:看 GPU 平均利用率就能判断负载均衡
平均利用率会掩盖局部热点。需要同时观察专家、设备、层和通信链路四个维度。
误区三:专家平均分配到 GPU 就是最佳放置
平均分配只平衡专家数量,不平衡实际 Token 流量。热门专家的组合和网络位置更重要。
误区四:检测到热点就立即迁移专家
迁移和复制专家会占用带宽、显存和初始化时间。没有连续窗口、冷却时间和回滚门槛,系统容易形成控制振荡。
误区五:训练数据上的路由统计可以直接用于生产
训练统计可以作为初始放置依据,但不能替代线上画像。真实业务域、语言和 Prompt 模板会改变专家激活分布。
上线检查清单
模型与路由
- 已记录每层、每专家的 Token 分布。
- 已验证 Top-K、共享专家和路由实现与模型配置一致。
- 已确认是否存在 Token 容量溢出或隐式丢弃。
- 已对主要业务域分别回放专家激活分布。
拓扑与通信
- 已绘制 GPU、NVLink/NVSwitch、PCIe 和跨节点网络拓扑。
- 已测量 All-to-All 的 P50/P95/P99。
- 已统计跨节点 Token 比例。
- 已验证专家放置不会把多个热点集中在同一设备。
- 已确认通信与计算是否能够有效重叠。
动态治理
- 热点检测使用连续窗口而非单点阈值。
- 专家副本受显存水位和迁移预算约束。
- 重排策略有冷却时间、滞回区间和回滚条件。
- Prefill 与 Decode 使用独立指标和门槛。
- 动态策略可以一键关闭并恢复静态映射。
灰度与回滚
- 使用真实流量影子回放验证新放置方案。
- 对比 TTFT、TPOT、吞吐、错误率和跨节点流量。
- 灰度期间保留旧专家映射。
- 模型版本、专家布局和路由策略可关联追溯。
- 达到 P95/P99 回退门槛时能够自动回滚。
FAQ
Expert Parallel 越大越好吗?
不是。更大的 EP 可以分散专家权重,但会扩大通信参与范围,并可能增加跨节点 All-to-All。应根据专家规模、单卡显存、网络拓扑和实际路由分布选择。
动态副本会改变模型输出吗?
如果副本权重完全一致,路由仅在等价副本之间选择,理论上不应改变模型语义。但实现中的数值精度、Kernel、同步顺序和版本不一致仍需通过回放验证。
如何判断瓶颈在专家计算还是通信?
同时采集每层专家计算时间、All-to-All 时间、等待时间和设备利用率。若专家计算较短但层延迟随跨节点流量增长,通常偏通信瓶颈;若单个专家计算时间和队列持续偏高,则更可能是专家热点。
参考资料
- DeepSpeed-MoE: Advancing Mixture-of-Experts Inference and Training to Power Next-Generation AI Scale
- Speculative MoE: Communication Efficient Parallel MoE Inference with Speculative Token and Expert Pre-scheduling
- Least-Loaded Expert Parallelism: Load Balancing An Imbalanced Mixture-of-Experts
- Scaling Multi-Node Mixture-of-Experts Inference Using Expert Activation Patterns
- Coordinated Scheduling for MoE LLM Serving
- Scalable Training of Mixture-of-Experts Models with Megatron Core