vLLM · 官方博客

vLLM 中的弹性专家并行

Elastic Expert Parallelism in vLLM

二〇二六年五月十九日 · 英文原文

vLLM 实现了弹性专家并行(Elastic EP),允许混合专家(MoE)模型在运行时通过添加或移除数据并行(DP)worker 来动态调整部署规模,无需重启服务器。该功能通过 `POST /scale_elastic_ep` API 触发,支持扩容和缩容,流程包括新 engine-core 初始化、备用通信组创建、专家映射与权重传输、拓扑切换及 EPLB 专家重新分配。Elastic EP 使用两阶段屏障协调跨 DP rank 的异步操作,并支持 NIXL EP 后端以减少重新初始化开销。该功能是 vLLM 容错方向(RFC #30112)的核心构建块,为故障检测、缩容移除故障 rank 及扩容恢复提供运行时重新配置路径。当前实现限制为 `tensor_parallel_size=1`、单 API 服务器且无 DBO,依赖 Ray DP 后端。

专家并行(Expert Parallelism,EP)是服务于混合专家(Mixture-of-Experts,MoE)模型以实现高吞吐量的关键技术。广泛的 EP 部署(即 EP 跨越多个 worker)能最大化 KV 缓存容量,从而实现极高的并发性或极长的上下文。这对于需要长上下文和高吞吐量的强化学习工作负载,以及多轮对话可能拉长上下文长度的 agentic 工作负载尤为重要。

在 vLLM 中,与许多其他推理框架一样,EP 是静态的:一旦部署启动,其服务容量就固定了。如果请求量超过该容量,vLLM 无法扩容以满足需求。如果需求下降,它也无法缩容以减少 GPU 使用和成本。唯一可行的选择是使用新配置完全重启,这很慢,并且可能丢弃大量流量。

弹性专家并行(Elastic EP)改变了这一点。它允许 vLLM 在运行时重新配置 worker 数量,因此 MoE 部署可以根据需求变化进行扩容或缩容,且对服务的干扰最小。

Elastic EP 通过添加或移除数据并行(Data-Parallel,DP)worker 来实现伸缩。在 vLLM 中,这会改变共享的专家并行(EP)组的大小以及专家在 worker 间的分布方式,我们将在背景部分解释。只需一个 API 调用即可:

curl -X POST http://localhost:8000/scale_elastic_ep \
  -H "Content-Type: application/json" \
  -d '{"new_data_parallel_size": 8}'

此 API 调用会将正在运行的部署从其当前的 DP 大小调整为 8 个 worker。

本文描述了 vLLM 中的 Elastic EP(RFC #20323PR #34861),包括扩容和缩容流程、vLLM 如何协调重新配置与正在执行的请求、该功能如何与 EPLB 和 EP 通信后端交互,以及为什么这项工作与 vLLM 新兴的容错方向高度相关。本文还讨论了 NIXL EP(PR #35627)作为一种后端,其通信模型与弹性重新配置和容错特别相关。

面向运维人员的 TL;DR:

  • Elastic EP 允许 vLLM 通过更改 DP 大小在运行时对 MoE 部署进行扩容或缩容,而无需重启服务器。
  • 您通过 POST /scale_elastic_ep 触发大小调整;vLLM 会重新配置实时拓扑并按需重新分配专家。
  • 此运行时重新配置路径是 vLLM 中容错服务的核心构建块。
  • NIXL EP 可以显著减少伸缩事件期间的重新初始化工作,并提供 EP 侧的故障检测、报告和恢复能力。

背景:专家并行与 DP Attention

在 MoE 模型中,attention 层仍然是密集的,而大多数前馈层被替换为稀疏的专家层,这些层将每个 token 路由到选定的专家集合。在深入探讨弹性伸缩之前,了解 Elastic EP 所依赖的两种并行策略会有所帮助。

数据并行(DP)Attention 使用请求级并行:每个 engine-core 处理不同的请求分片,并维护自己的 KV 缓存和调度器。这在 MLA 等架构中尤其有用,因为在这些架构中,张量并行(TP)会在 GPU 间重复 KV 缓存,浪费内存并限制批处理大小。

专家并行(EP) 用于专家层。专家不是跨 GPU 分片,而是分布在不同 GPU 上,token 仅被分发到拥有所选专家的 GPU。

在 vLLM 中,attention 在每个 DP worker 上独立运行,而专家层在这些 worker 之间共享一个 EP 组(EP 组大小为 DP x TP)。Elastic EP 在运行时更改 DP worker 的数量,从而相应地缩放 EP 组并跨组重新分配专家。

挑战:需要更改哪些状态?

在运行时伸缩 DP 不仅仅是启动或终止进程的问题。EP 大小的变化会使多个运行时状态失效:

因此,实现将伸缩视为一个协调的状态机。每个阶段都有明确的同步点,并且这些同步点必须与模型前向执行安全共存。

扩容流程

DP=N 扩容到 DP=M(其中 M > N)比缩容更复杂,因为需要将新的 rank 引入正在运行的部署。

1. 触发与请求处理

操作从 /scale_elastic_ep 开始。如果设置了 VLLM_ELASTIC_EP_DRAIN_REQUESTS=1,vLLM 首先会等待正在处理的请求排空,最多等待 drain_timeout 秒(默认 120 秒)。否则,立即进行伸缩。

2. 新 Engine Core 初始化

启动新的 engine-core worker 依赖于 Ray DP 后端。在扩容期间,Ray DP 后端会在当前可用的 GPU 上启动目标 DP 大小所需的额外 DP worker。新的 rank 接收当前的专家映射,并使用占位权重初始化模型。然后它们等待后续的传输和重新配置阶段,以将它们带入活动拓扑。

就绪状态分两个阶段协调:一个信号允许现有 rank 创建备用组,稍后的信号允许开始权重传输。

3. 备用通信组

一个关键的设计选择是,vLLM 不会立即拆除活动的通信组。相反,现有的 rank 首先创建跨越目标 rank 集合的备用组。这些组使用 StatelessGroupCoordinator 创建,它独立于 PyTorch 的全局 WORLD 状态。

这使得在切换之前准备新配置成为可能,同时旧配置仍可以执行前向传播。

使用 nixl_ep,这种转换可以是增量的:vLLM 无需拆除并重新创建所有 EP 侧连接,而是可以通过 NIXL EP 的 connect_ranks() / disconnect_ranks() API 添加或移除 rank,同时保持现有连接不受影响。

4. 专家映射与权重传输

一旦备用组存在,我们使用它们将当前的专家映射广播出去,并将非专家权重从现有 rank 传输到新 rank,传输工作尽可能均匀地分布在现有 rank 上。Elastic EP 复用了 EPLB 用于专家权重移动的相同 GPU 到 GPU 发送/接收路径,但将其扩展到 attention 层、归一化层、embedding 层和其他非专家权重,使用可用的高速互连,例如节点内的 NVLink 或跨节点的 RDMA。

专家权重在此阶段不会被移动。它们将在新拓扑激活后由 EPLB 传输。在转换期间,常规的 EPLB 活动会暂停,以免干扰重新配置。

5. 切换

切换是所有 rank 停止使用旧拓扑并开始使用新拓扑的时刻。在此阶段,vLLM 会:

  1. 释放 CUDA graphs 并重置 torch.compile 状态。
  2. 将备用组提升为活动的 EP、DP 和 world 组。
  3. 销毁旧组。
  4. 为新的 EP 大小重新配置 MoE 模块。
  5. 重新预热模型,使 CUDA graphs 和编译路径与新设置匹配。

引擎协调状态,例如运行标志、wave 计数器和步骤计数器,会在新的 DP 组中同步,以便每个 rank 从一致的点恢复。

此时,新的 rank 已成为活动 DP 组的一部分,可以参与前向传播并运行 attention,但它们尚未拥有专家。专家所有权会在后续的 EPLB 重新洗牌中更新。

6. EPLB 重新洗牌

新拓扑激活后,EPLB 会在所有 M 个 rank 之间重新分配专家。这会更新专家映射并执行新布局所需的专家权重移动。重新洗牌完成后,常规的 EPLB 操作恢复。

缩容流程

DP=M 缩容到 DP=N 遵循与扩容相同的一般模式,但有一个重要区别:EPLB 重新洗牌必须首先发生。即将被移除的 rank 可能仍然拥有专家权重,因此所有 M 个 engine-core 首先参与一次重新洗牌,将专家整合到 N 个幸存的 rank 上,并将任何必要的专家权重从即将离开的 rank 迁移出去。

跨 DP Rank 协调重新配置步骤

一个微妙的问题是,DP engine-core 是异步运行的,因此它们可能在不同时间收到重新配置通知。当某些 rank 到达下一个 Elastic EP 阶段时,其他 rank 可能已经开始多执行一个前向步骤。如果较早的 rank 立即继续,组将在重新配置和前向执行之间分裂,从而导致部署死锁。

Elastic EP 通过两阶段屏障来处理这个问题。第一个屏障使用超时:如果它没有及时完成,到达的 rank 推断某些对等方已经进入了另一个引擎步骤,因此它们也返回引擎循环再进行一次迭代,而不是单独继续。在下一次迭代中,一旦所有 rank 到达相同的边界,第二个屏障(没有超时路径)允许它们一起进入下一阶段。

通往容错之路

Elastic EP 是容错的核心构建块,因为它为 vLLM 提供了故障后所需的运行时重新配置路径。如果一个 rank 宕机,Elastic EP 提供了必要的缩容和扩容路径来移除该 rank、重新分配其专家,并在之后添加替代容量,而无需重启整个部署。这是 RFC #30112 中讨论的更广泛容错方向的一部分。

在高层面上,恢复流程如下所示:

  1. 检测 通过健康检查或后端特定的故障信号来检测故障。
  2. 缩容 以移除故障 rank 并重新分配其专家。
  3. 扩容 一旦替代容量可用,再次扩容。

NIXL EP 在这里也很相关,因为它可以检测、报告和恢复 EP 侧的故障,以及在容量再次可用时重新连接替代 rank。

后续步骤

Elastic EP 已经提供了核心的运行时重新配置路径,但当前的实现仍然有相当特定的范围和一些明显的后续领域:

入门指南

启用 Elastic EP 启动

下面的示例使用 DeepSeek-V2-Lite-Chat 作为小型 MoE 示例。当前实现针对 tensor_parallel_size=1、一个 API 服务器且没有 DBO 的 Ray DP 部署。

vllm serve deepseek-ai/DeepSeek-V2-Lite-Chat \
    --trust-remote-code \
    --tensor-parallel-size 1 \
    --data-parallel-size 2 \
    --data-parallel-backend ray \
    --api-server-count 1 \
    --enable-expert-parallel \
    --enable-elastic-ep \
    --enable-eplb \
    --eplb-config.num_redundant_experts 0 \
    --all2all-backend allgather_reducescatter \
    --gpu-memory-utilization 0.8

运行时扩容

使用 Ray DP 后端,添加容量可以简单到将另一个节点加入 Ray 集群;一旦 Ray 看到新的 GPU,Elastic EP 就可以在运行时将部署扩展到它们上面。

例如,在一个新的 worker 节点上:

ray start --address="${HEAD_NODE_IP}:6379"

curl -X POST http://localhost:8000/scale_elastic_ep \
  -H "Content-Type: application/json" \
  -d '{"new_data_parallel_size": 16}'

缩容

curl -X POST http://localhost:8000/scale_elastic_ep \
  -H "Content-Type: application/json" \
  -d '{"new_data_parallel_size": 8}'

使用 NIXL EP 作为通信后端

如果您想将 NIXL EP 与 Elastic EP 一起使用:

uv pip install nixl
 
vllm serve deepseek-ai/DeepSeek-V2-Lite-Chat \
    --trust-remote-code \
    --tensor-parallel-size 1 \
    --data-parallel-size 2 \
    --data-parallel-backend ray \
    --api-server-count 1 \
    --enable-expert-parallel \
    --enable-elastic-ep \
    --enable-eplb \
    --all2all-backend nixl_ep

有关安装详细信息和传输配置,请参阅 NIXL 仓库

参考文献

致谢

感谢所有为将 Elastic EP 引入 vLLM 做出贡献的人。

译自 vLLM · 官方博客 · 录于 二〇二六年五月十九日