Prhub

#35061 [Fix] Select custom all-reduce v2 by topology capability

原始 PR 作者 charlotte12l 合并时间 2026-08-19 01:30 文件变更 8 提交数 3 评论 2 代码增减 +268 / -236

执行摘要

修复 TP16 自定义 all-reduce v2 被多节点策略误禁的回归

32541 引入的 TP2-8 专用多节点策略在能力检测前对 TP16 禁用了 SGLANG_OPT_USE_CUSTOM_ALL_REDUCE_V2,迫使本可合资格运行的配置回退。PR body 明确指出:"Before that PR, custom all-reduce v2 was enabled by default and eligible TP16 groups reached the topology and allocator capability check"。因此本 PR 移除多余策略,让 SGLANG_OPT_USE_CUSTOM_ALL_REDUCE_V2 恢复为唯一 enable / opt-out 控制点,并由拓扑、VMM allocator 能力和架构调优的 world size 决定跨节点 v2 选择。

值得精读。核心看点是能力来源单一化:world size 支持范围由架构配置表 keys 派生,避免硬编码 2..16 与真实调优表不一致;以及“跨节点能力 = NVLink clique ∩ VMM allocator”的判定设计,直接对应 MNNVL fabric 的物理传输前提。建议合入后观察 TP16 MNNVL 与 TP8 MNNVL 的性能监控,并补一个公开 benchmark;同时留意 CI Extra 失败。

讨论亮点

PR 没有任何 review comments,merrymercy 直接 APPROVED,说明改动虽涉及分布式通信但评审路径清晰。Issue 区 nvpohanh 将 @ajit283 加入关注,但未产生公开技术讨论。值得注意的未解事项是 PR Test (Extra) 有一个失败任务(Run #31978608691),公开讨论中未给出解释。

实现拆解

  1. 收敛启用开关:删除 python/sglang/srt/server_args.py 中的 ServerArgs._handle_custom_all_reduce_v2_multinode 方法及其在 _handle_environment_variables 中的调用,并同步删除 python/sglang/srt/environ.pySGLANG_ENABLE_CUSTOM_ALL_REDUCE_V2_MULTINODE 的定义。理由:多节点是否可用应由运行时拓扑与分配器能力决定,而不是由启动参数策略在能力检测前先斩断;这样 tp_size <= 8 这类近似条件不再误伤 TP16。

  2. 配置表重构python/sglang/srt/distributed/device_communicators/configs/custom_all_reduce_v2.py 中,将按 world size 单次构建配置的 _sm90_config / _sm100_config 重构为按 num_sm 缓存的 _sm90_configs(num_sm) / _sm100_configs(num_sm),一次生成该架构下全部受支持 world size 的 AllReduceConfig;新增 _get_all_reduce_configs() 聚合入口、get_all_reduce_config(world_size) 查表、get_supported_world_sizes() 从配置表 keys 推导支持范围。这样 SM90 仅支持 2..8,SM100 才包含 16,TP16 可用性与架构直接绑定。

  3. 能力检测改写python/sglang/srt/distributed/device_communicators/custom_all_reduce_v2.pycan_use_custom_all_reduce_v2 不再读取 multinode 环境变量。节点内继续走 can_use_custom_all_reduce_with_nvlink;跨节点则要求 is_one_nvlink_clique(group, device)_is_vmm_backed_allocator(device) 同时成立,因为 graph zero-copy 依赖 IPC handle 仅限节点内,跨节点 eager 路径依赖 FABRIC / POSIX-fd VMM handle。

  4. 派发顺序调整python/sglang/srt/distributed/parallel_state.py 中让合资格的自定义 all-reduce 输入先于 symmetric-memory PyNccl 运行,较大消息仍回退;配套测试 test_custom_allreduce_precedes_symmetric_memory_pynccl 验证 ca_comm 被选中时不会调用 pynccl_comm.all_reduce

  5. 测试迁移与配套:删除 test/registered/unit/server_args/test_mnnvl_auto_inference.py(4 个 server-args 策略用例);新增 test/registered/unit/distributed/test_custom_all_reduce_v2_capability.py(拓扑能力矩阵,覆盖跨节点 fabric + VMM 组合);扩充 test/registered/unit/distributed/test_parallel_state.py。本地相关测试 10 个全部通过;无模型数学变更,未提供公开速度基准。

文件 模块 状态 重要度
python/sglang/srt/distributed/device_communicators/configs/custom_all_reduce_v2.py 通信配置 modified 8.57
python/sglang/srt/distributed/device_communicators/custom_all_reduce_v2.py 通信能力 modified 6.74
python/sglang/srt/server_args.py 服务参数 modified 6.56
python/sglang/srt/environ.py 环境变量 modified 4.47
python/sglang/srt/distributed/parallel_state.py 并行状态 modified 5.87
test/registered/unit/distributed/test_custom_all_reduce_v2_capability.py 能力测试 added 7.13
test/registered/unit/server_args/test_mnnvl_auto_inference.py 参数测试 removed 6.51
test/registered/unit/distributed/test_parallel_state.py 派发测试 modified 5.52

关键符号

can_use_custom_all_reduce_v2 _is_vmm_backed_allocator get_supported_world_sizes _sm90_configs _sm100_configs _get_all_reduce_configs get_all_reduce_config

关键源码片段

python/sglang/srt/distributed/device_communicators/custom_all_reduce_v2.py dependency-wiring

能力检测核心入口:跨节点启用条件改为 NVLink clique + VMM allocator,直接决定 TP16 能否恢复 v2。

# 检查当前缓存分配器是否由 expandable segments 的 VMM 支撑。
# 多节点 zero-copy 依赖 FABRIC / POSIX-fd VMM handle,因此这是跨节点放行的前置条件。
def _is_vmm_backed_allocator(device: torch.device) -> bool:
    probe = torch.empty(1, dtype=torch.uint8, device=device)
    return is_vmm_pointer(probe.data_ptr())
​
​
def can_use_custom_all_reduce_v2(
    group: ProcessGroup,
    device: torch.device,
) -> bool:
    # 支持范围不再硬编码 2..16,而从当前架构配置表 keys 推导:
    # SM90 只有 2..8,SM100 才有 16,Hopper TP16 不会被错误放行。
    supported = get_supported_world_sizes()
    if dist.get_world_size(group=group) not in supported:
        return False
​
    # 跨节点(MNNVL):节点内 NVLink 检查不适用,真正门槛是
    # single NVLink clique + VMM allocator 同时成立;否则回退旧实现。
    if not all(in_the_same_node_as(group, source_rank=0)):
        return is_one_nvlink_clique(group, device) and _is_vmm_backed_allocator(device)
​
    # 节点内路径保持原有 NVLink 能力检查
    full_nvlink = can_use_custom_all_reduce_with_nvlink(
        group=group,
        device=device,
        supported_world_size=list(supported),
        cls_name="CustomAllReduceV2",
    )
    return full_nvlink is True
test/registered/unit/distributed/test_custom_all_reduce_v2_capability.py test-coverage

新增能力矩阵测试,锁定跨节点 fabric + VMM 的组合条件,防止能力检测退化为恒真或恒假。

# 拓扑能力矩阵测试:same_node=False 表示跨节点,此时必须同时满足
# single NVLink clique 与 VMM allocator;same_node=True 时不要求 fabric / VMM。
@pytest.mark.parametrize(
    ("same_node", "has_fabric_clique", "uses_vmm", "expected"),
    [
        (False, True, True, True),
        (False, False, True, False),
        (False, True, False, False),
        (True, None, None, True),
    ],
)
def test_topology_capability(
    monkeypatch, same_node, has_fabric_clique, uses_vmm, expected
):
    # 跨节点用例用 world size 16(覆盖本次修复的 TP16 场景),节点内用 8
    world_size = 8 if same_node else 16
    group, device = _patch_group(
        monkeypatch,
        world_size=world_size,
        same_node=same_node,
    )
​
    # 节点内分组不应触碰 fabric clique 与 VMM 探测,用 pytest.fail 约束调用面
    def is_one_clique(group, device):
        if same_node:
            pytest.fail("intra-node groups do not need a fabric clique")
        return has_fabric_clique
​
    def is_vmm_backed(device):
        if same_node:
            pytest.fail("intra-node groups do not need VMM")
        return uses_vmm
​
    intra_node_capability = Mock(return_value=True)
    monkeypatch.setattr(custom_all_reduce_v2, "is_one_nvlink_clique", is_one_clique)
    monkeypatch.setattr(custom_all_reduce_v2, "_is_vmm_backed_allocator", is_vmm_backed)
    monkeypatch.setattr(
        custom_all_reduce_v2,
        "can_use_custom_all_reduce_with_nvlink",
        intra_node_capability,
    )
​
    assert custom_all_reduce_v2.can_use_custom_all_reduce_v2(group, device) is expected
    if same_node:
        intra_node_capability.assert_called_once_with(
            group=group,
            device=device,
            supported_world_size=[world_size],
            cls_name="CustomAllReduceV2",
        )
    else:
        intra_node_capability.assert_not_called()

评论区精华

评审与 CI 状态 other

PR 无 review comments;merrymercy 直接 APPROVED。Issue 区 nvpohanh cc @ajit283 后未展开技术讨论;EXTRA CI 有一个失败任务未在公开讨论中解释。

结论:无遗留技术争议,已合入 main;EXTRA CI 失败需另行排查。 · merged

风险与影响

  1. 派发顺序变化parallel_state.py 让自定义 all-reduce 优先于 symmetric-memory PyNccl,影响所有 TP 规模的默认路径,虽然保留 fallback,但缺少端到端 benchmark,存在性能回归未被量化的可能。
  2. 运行时探测依赖:跨节点放行依赖 is_one_nvlink_clique_is_vmm_backed_allocator 的运行时探测;探测不准可能导致错误回退(性能损失)或错误启用,测试覆盖了 False / False 分支但未覆盖真实硬件组合。
  3. 支持范围推导get_supported_world_sizes() 从配置表 keys 推导,未来新增架构配置时若漏写某个 world size,会静默跳过 v2。
  4. 测试保护变化test_mnnvl_auto_inference.py 删除后,server-args 层不再有保护性断言,若后续重新引入类似策略,只能靠 capability 测试间接覆盖。
  5. CI 未决问题:EXTRA CI 存在失败任务且未在讨论中解释,需确认是否为偶发环境问题。

用户侧:GB200 / GB300 MNNVL TP16 等「跨节点 single clique + VMM」部署恢复自定义 all-reduce v2 加速;非 fabric 多节点和 Hopper TP16 继续回退到 legacy 路径。运维侧:环境变量契约简化,SGLANG_ENABLE_CUSTOM_ALL_REDUCE_V2_MULTINODE 被移除,依赖它的脚本需同步更新。系统侧:all-reduce 派发优先级变化可能影响与 PyNccl / symmetric-memory 的交互,需要观察 TP8 / TP16 的吞吐与延迟指标。组织侧:测试从启动参数策略迁移到能力矩阵,降低 server-args 维护成本,但需要确认 CI Extra 失败。

通信核心路径变更 运行时能力探测依赖 缺少公开性能基准 EXTRA CI 失败未解释

关联 Issue

#32541 [Kimi] Support kimi-k3

完整报告

参与讨论