Prhub

#2220 Optimize update weight

原始 PR 作者 zhuzilin 合并时间 2026-07-19 10:16 文件变更 15 提交数 2 评论 0 代码增减 +711 / -37

执行摘要

优化 MoE 权重更新,引入专家路由跳过非专家转换

原有权重更新机制对所有参数统一进行 Megatron-to-HF 转换和广播,在 MoE 模型上存在大量冗余通信和转换。通过引入专家路由,可以识别每个专家参数应发送到的引擎 rank,只传输必要的权重;结合选择性转换进一步跳过非专家权重,从而降低权重更新延迟和网络负载。

建议架构师和训练引擎维护者仔细阅读 expert_routing.py 中的拓扑差分检测和路由规划逻辑,以及 update_weight_from_tensor.py 中如何组合选择性转换。测试部分仍可加强,特别是多节点异构拓扑场景。

实现拆解

  1. 新增专家路由模块 (slime/backends/megatron_utils/update_weight/expert_routing.py):定义 _SGLangMoeTopology 描述引擎并行拓扑,_can_route_experts 判断路由条件,configure_expert_routing 规划每个专家参数的目标 rank。
  2. 集成到权重更新主流程 (update_weight_from_tensor.py):在 __init__ 中获取 _full_param_info_bucketsconnect_rollout_engines 增加 engine_parallel_configs 参数;新增 _build_flattened_tensor_data 安全地构建平坦化张量;新增 _prepare_expert_weight_batch_update_expert_weights 处理专家权重发送。
  3. 支持选择性转换 (hf_weight_iterator_direct.py):get_hf_weight_chunks 新增 should_convert_chunk 参数,允许跳过非专家 chunk 的 HF 转换;提取 pack_param_info_buckets 复用。
  4. 暴露引擎并行配置 (rollout.py):ServerGroup 新增 parallel_config() 方法,返回 tp/pp/ep/moe_dp 配置;RolloutServer 新增 engine_parallel_configs 属性透传到权重更新模块。
  5. 接口适配 (hf_weight_iterator_base.py, hf_weight_iterator_bridge.py):抽象方法增加 param_info_buckets 参数,桥接器抛出不支持异常。
  6. 测试新增test_sglang_config.py 验证并行配置推导,test_empty_colocated_weight_bucket.py 保障空权重桶场景。
文件 模块 状态 重要度
slime/backends/megatron_utils/update_weight/expert_routing.py 权重更新 added 9.25
slime/backends/megatron_utils/update_weight/update_weight_from_tensor.py 权重更新 modified 8.43
slime/backends/megatron_utils/update_weight/hf_weight_iterator_direct.py 权重更新 modified 7.86
slime/ray/rollout.py 调度器 modified 7.13
slime/backends/megatron_utils/update_weight/hf_weight_iterator_base.py 权重更新 modified 6.45
slime/backends/megatron_utils/update_weight/hf_weight_iterator_bridge.py 权重更新 modified 6.41
slime/backends/megatron_utils/update_weight/common.py 权重更新 modified 6.37
slime/backends/sglang_utils/sglang_engine.py 引擎 modified 5.74
slime/backends/megatron_utils/hf_checkpoint_saver.py 检查点 modified 5.39
tests/utils/test_sglang_config.py 测试 modified 6.37
tests/test_empty_colocated_weight_bucket.py 测试 modified 4.9
slime/backends/megatron_utils/actor.py 后端 modified 4.82

关键符号

configure_expert_routing _get_sglang_moe_topology _get_homogeneous_sglang_moe_topology _can_route_experts _build_flattened_tensor_data _prepare_expert_weight_batch _update_expert_weights get_hf_weight_chunks _convert_to_hf_named_tensors pack_param_info_buckets parallel_config engine_parallel_configs

关键源码片段

slime/backends/megatron_utils/update_weight/expert_routing.py dependency-wiring

新增模块,实现专家路由核心逻辑,包括拓扑检测、可行性判断和传输规划。

# 注意: 以下代码定义 SGLang MoE 拓扑类型和相关检查函数from dataclasses import dataclass
from typing import Any, Mapping, Sequence
from argparse import Namespace@dataclass(frozen=True)
class _SGLangMoeTopology:
    tp_size: int
    pp_size: int
    ep_size: int
    moe_dp_size: intdef _config_value(
    parallel_config: Mapping[str, Any] | None,
    key: str,
    default: Any,
) -> Any:
    # 从 parallel_config 或 default 中取值
    if parallel_config is None:
        return default
    return parallel_config.get(key, parallel_config.get(key.replace('_', '-'), default))def _get_sglang_moe_topology(
    args: Namespace,
    engine_gpu_count: int,
    parallel_config: Mapping[str, Any] | None = None,
) -> _SGLangMoeTopology:
    # 根据参数和引擎 GPU 数量计算拓扑
    pp_size = int(_config_value(parallel_config, 'pp_size', getattr(args, 'sglang_pp_size', 1)))
    default_tp_size = engine_gpu_count // pp_size
    tp_size = int(_config_value(parallel_config, 'tp_size', default_tp_size))
    ep_size = int(_config_value(parallel_config, 'ep_size', getattr(args, 'sglang_ep_size', 1)))
    moe_dp_size = int(_config_value(parallel_config, 'moe_dp_size', getattr(args, 'sglang_moe_dp_size', 1)))
    return _SGLangMoeTopology(tp_size, pp_size, ep_size, moe_dp_size)def _can_route_experts(
    args: Namespace,
    sglang_moe_topology: _SGLangMoeTopology,
    engine_gpu_counts: Sequence[int],
) -> bool:
    # 检查是否可以进行专家路由:需要 raw 模式、PP=1、EP>1、无冗余专家等
    return (
        getattr(args, 'megatron_to_hf_mode', 'raw') == 'raw'
        and sglang_moe_topology.pp_size == 1
        and sglang_moe_topology.ep_size > 1
        and not getattr(args, 'sglang_enable_eplb', False)
        and getattr(args, 'sglang_ep_num_redundant_experts', 0) == 0
        and getattr(args, 'sglang_init_expert_location', 'trivial') == 'trivial'
        and not getattr(args, 'sglang_enable_elastic_expert_backup', False)
        and mpu.get_expert_tensor_parallel_world_size() == 1
        and _sglang_moe_tp_is_one(engine_gpu_counts, sglang_moe_topology)
    )
slime/backends/megatron_utils/update_weight/update_weight_from_tensor.py dependency-wiring

集成专家路由,新增选择性转换和扁平化张量构建,是权重更新主入口。

# 注意: 构建平坦化张量数据,避免重用 buffer 引发数据竞争def _build_flattened_tensor_data(
    named_tensors: list[tuple[str, torch.Tensor]],
) -> dict[str, Any]:
    if not named_tensors:
        return {
            'flattened_tensor': torch.empty(0, dtype=torch.uint8, device=torch.cuda.current_device()),
            'metadata': [],
        }
    # 每次创建新的 FlattenedTensorBucket,确保不与前一次共享 buffer
    flattened_tensor_bucket = FlattenedTensorBucket(named_tensors=named_tensors)
    return {
        'flattened_tensor': flattened_tensor_bucket.get_flattened_tensor(),
        'metadata': flattened_tensor_bucket.get_metadata(),
    }# 在 __init__ 中从 hf_weight_iterator 获取完整和专家参数桶
self._hf_weight_iterator = HfWeightIteratorBase.create(args, model, model_name, quantization_config)
param_info_buckets = getattr(self._hf_weight_iterator, 'megatron_local_param_info_buckets', None)
self._full_param_info_buckets = (
    tuple(tuple(bucket) for bucket in param_info_buckets) if param_info_buckets is not None else None
)
self._non_expert_param_info_buckets: list[list[ParamInfo]] | None = None
slime/ray/rollout.py core-logic

暴露引擎并行配置,供权重更新模块使用。

# 注意: 根据 overrides 和 args 推导并行配置
def parallel_config(self) -> dict[str, Any]:
    overrides = {key.replace('-', '_'): value for key, value in self.sglang_overrides.items()}
    pp_size = int(overrides.get('pp_size', getattr(self.args, 'sglang_pp_size', 1)))
    tp_size = int(overrides.get('tp_size', self.num_gpus_per_engine // pp_size))
    return {
        'tp_size': tp_size,
        'pp_size': pp_size,
        'ep_size': int(overrides.get('ep_size', getattr(self.args, 'sglang_ep_size', 1))),
        'moe_dp_size': int(overrides.get('moe_dp_size', getattr(self.args, 'sglang_moe_dp_size', 1))),
    }@property
def engine_parallel_configs(self) -> list[dict[str, Any]]:
    """Per-engine SGLang parallel config, parallel to engines."""
    return [g.parallel_config() for g in self.server_groups for _ in g.engines]

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

专家路由的合法性依赖引擎拓扑的一致性假设,若 engine_parallel_configs 配置错误或引擎之间拓扑异构未被检测到,可能导致权重发送错误目标;should_convert_chunk 的判断条件若漏掉某些 chunk 会导致未转换的权重被发送;_build_flattened_tensor_data 每次创建新 buffer 增加了短暂内存峰值,但避免了并发写入风险。

主要影响带有 MoE 的训练任务,权重更新性能提升;非 MoE 模型行为不变(路由条件不满足时回退旧路径)。需要调用方在 connect_rollout_engines 时额外传入 engine_parallel_configs,否则保留向后兼容。

专家路由依赖拓扑一致 选择性转换可能遗漏参数 新增内存峰值

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论