专家并行负载均衡器(EPLB)#
为什么需要EPLB?#
在使用专家并行(EP)时,不同的专家被分配到不同的NPU上。由于不同专家的负载可能随当前工作负载变化,确保各NPU间负载均衡至关重要。我们采用冗余专家策略,复制负载较重的专家,然后通过启发式方法将这些复制后的专家分配到各NPU上,以实现负载均衡。此外,得益于MoE模型中使用的组限专家路由机制,我们还尽可能将同一组的专家放置在同一节点上,以减少节点间的数据传输。
为便于复现和部署,Vllm Ascend 在 vllm_ascend/eplb/core/policy 中提供了已部署的EP负载均衡算法。该算法基于预估的专家负载,计算出均衡的专家复制与放置方案。请注意,具体的专家负载预测方法不在本代码库的讨论范围内。一种常用方法是使用历史统计数据的移动平均值。

如何使用EPLB?#
详细使用说明请参阅用户指南中的EPLB章节:如何使用EPLB
工作原理#
EPLB模块架构
vllm_ascend
├── eplb
│ ├── adaptor
│ │ ├── abstract_adaptor.py
│ │ ├── vllm_adaptor.py
│ ├── core
│ │ ├── policy
│ │ │ ├── policy_abstract.py
│ │ │ ├── policy_dynamic_ep.py
│ │ │ ├── policy_dynamic_ep_v2.py
│ │ │ ├── policy_factory.py
│ │ │ ├── policy_flashlb.py
│ │ ├── eplb_device_transfer_loader.py
│ │ ├── eplb_utils.py
│ │ ├── eplb_worker.py
│ ├── eplb_updator.py
│ ├── utils.py
└───────────
1. 适配器模块 处理不同MoE模型类型的注册与适配
abstract_adaptor.py定义EPLB适配器统一注册接口的抽象基类vllm_adaptor.py支持Qwen3-MoE和DeepSeek模型的实现,标准化策略算法的参数处理
2. 核心模块 实现核心算法、更新与异步处理
策略子模块 采用工厂模式实例化的负载均衡算法
policy_abstract.py负载均衡策略接口的抽象类policy_dynamic_ep.py开源EPLB论文算法的默认实现policy_dynamic_ep_v2.py优化专家交换的增强版本,适用于低带宽设备(如A2)policy_flashlb.py基于阈值的调整策略,通过逐层波动检测降低操作成本policy_factory.py自动算法实例化的策略工厂
eplb_device_transfer_loader.py管理专家表/权重的传输与更新eplb_utils.py专家表初始化与映射的实用工具eplb_worker.py异步算法编排与结果处理
3. 系统组件
eplb_updator.py推理工作流中负载均衡的中央协调器utils.pyEPLB接口注册的通用实用工具
关键优化点:
保持原始结构的同时提升了技术表述的清晰度
标准化术语
通过简洁的描述符增强了算法区分度
通过分层呈现改进了范围界定
在优化可读性的同时保留了文件/类的关系
默认算法#
分层负载均衡#
当服务器节点数量能整除专家组数量时,我们采用分层负载均衡策略以利用组限专家路由。首先将专家组均匀分配到各节点,确保节点间负载均衡。然后在每个节点内部复制专家。最后将复制后的专家分配到各NPU上,确保NPU间负载均衡。分层负载均衡策略可在专家并行规模较小的预填充阶段使用。
全局负载均衡#
在其他情况下,我们采用全局负载均衡策略,该策略无视专家组的划分,全局复制专家,并将复制后的专家分配到各NPU上。此策略可在专家并行规模较大的解码阶段采用。
添加新的EPLB策略#
若要在vllm_ascend中添加新的EPLB策略,必须遵循以下步骤:
继承
policy_abstract.py中的EplbPolicy抽象类,并重写rebalance_experts接口,确保输入参数current_expert_table、expert_workload和返回类型newplacement保持一致。例如:
class RandomLoadBalance(EplbPolicy):
def __init__(self, config: DynamicConfig):
super().__init__(config)
def rebalance_experts(self, current_expert_table, expert_workload):
new_table = copy.deepcopy(current_expert_table)
num_layers = len(current_expert_table)
for i in range(num_layers):
# randomly choose two card
# indices = random.sample(range(num_card), 2)
indices = [3, 1]
# swap redundant experts
expert_id_to_exchange = new_table[i][indices[0]][-1].clone()
new_table[i][indices[0]][-1] = new_table[i][indices[1]][-1]
new_table[i][indices[1]][-1] = expert_id_to_exchange
return 1, [-i for i in range(num_layers)], new_table
要添加新的EPLB算法,需在
policy_factory.py的PolicyFactory中包含策略类型及其对应的实现类。
添加新的MoE模型#
模型集成实现指南
适配器文件修改
继承或修改
vllm_ascend/eplb/adaptor/vllm_adaptor.py为关键参数添加处理逻辑:
num_dense_layersglobal_expert_numnum_roe_layers
确保
model_register函数中的参数同步。例如:
修改
vllm_adaptor.py的__init__方法,添加新MoE模型的EPLB参数:if self.model.config.model_type == "qwen3_moe": self.num_dense_layers = 0 self.global_expert_num = self.model.config.num_experts
修改
vllm_adaptor.py的model_register方法,为新MoE模型注册EPLB参数:if config.model_type == "qwen3_moe": model.num_moe_layers = config.num_hidden_layers
MoE特性集成
在
vllm_ascend/eplb/utils.py中扩展MoE特定的方法实现专家路由或权重管理所需的功能
注册逻辑更新
在
model_register函数中添加补丁逻辑保持与现有模型类型的向后兼容性
验证与测试
验证各层间的参数一致性
测试专家表的跨设备通信
与基线实现(如Qwen3-MoE)进行基准测试
关键实现说明:
在抽象类中保留现有的接口契约
使用装饰器进行非侵入式的补丁集成
利用
eplb_utils.py处理共享的专家映射操作
可维护性(DFX)#
参数验证#
整数参数#
所有整数输入参数必须明确指定其最大值和最小值,并接受有效值验证。例如,num_iterations_eplb_update 必须大于0:
@staticmethod
def check_iterations(iterations):
if not isinstance(iterations, int):
raise TypeError(f"The {iterations} is not int.")
if iterations <= 0:
raise ValueError(
f"The {iterations} can not less than or equal to 0.")
if iterations > sys.maxsize:
raise ValueError(
f"The {iterations} can not large than {sys.maxsize}")
文件路径#
必须检查EPLB文件路径的合法性,例如文件路径是否有效,是否具有适当的读写权限。例如:
@staticmethod
def check_expert_map_path(expert_map):
if expert_map is None:
return
if not isinstance(expert_map, str):
raise TypeError("The expert_map is not str.")
if not expert_map.strip():
raise ValueError("The expert_map is not empty.")
_, ext = os.path.splitext(expert_map)
if ext.lower() != ".json":
raise TypeError("The expert_map is not json.")
if not os.path.exists(expert_map):
raise ValueError("The expert_map is not exist.")
try:
with open(expert_map, "w", encoding='utf-8') as f:
f.read()
except Exception as e:
raise IOError(
f"Fail read expert info from {expert_map}, please check the reading permission of {expert_map} : {e}"
)
函数规范#
初始化函数#
初始化时,所有EPLB参数必须默认初始化,并指定参数类型和默认值以确保正确处理。
通用函数#
所有方法参数必须指定参数类型和默认值,函数必须包含对默认参数的默认返回值处理。建议使用 try-except 块处理函数体,指定捕获的异常类型和失败处理方式(例如记录异常或返回失败状态)。
一致性#
专家映射#
专家映射在初始化和更新时必须保持全局唯一性。在初始化时的多节点场景中,应使用分布式通信来验证各排名(rank)间专家映射的一致性。若不一致,应通知用户哪些排名存在映射不一致。在更新过程中,若仅少数层或某排名的专家表发生更改,必须将更新后的专家表与EPLB的上下文同步,以确保全局一致性。
专家权重#
更新专家权重时,必须确保为专家权重分配的内存已被释放,或专家(指旧版本)不再被使用。
限制条件#
使用EPLB前,启动脚本并添加 export DYNAMIC_EPLB="true"。在执行负载数据收集(或性能数据收集)前,启动脚本并添加 export EXPERT_MAP_RECORD="true"。