Prhub

#51635 [ROCm][Bugfix] Use TCP store when AITER custom all-reduce is enabled

原始 PR 作者 vllmellm 合并时间 2026-08-10 19:07 文件变更 3 提交数 3 评论 9 代码增减 +34 / -3

执行摘要

ROCm 启用 AITER 时回退 TCP store,修复启动崩溃

PR body 明确指出:#50999 把单节点 executor 从 TCP 切换到 file:// rendezvous 后,ROCm 上启用 AITER custom all-reduce 的配置在 worker init 阶段必然失败,报错 AssertionError: IPC metadata exchange requires a pure-TCP KV store (torch.distributed.TCPStore), got FileStore.。原因是 AITER 的 custom_all_reduce.py 断言默认 store 必须是 TCPStore,而 file:// rendezvous 产生的是 FileStore,AITER 上游尚未接受 FileStore,因此需要为这个精确配置恢复 #50999 之前的 TCP 行为作为过渡。

值得精读。该 PR 展示了如何用“单一判定函数 + 双执行器条件分支”的方式,在不破坏默认路径的前提下为特定后端保留旧行为,是典型的平台兼容性 stopgap 写法。阅读时可重点看 aiter_requires_tcp_store() 的收敛设计,以及 review 中关于 loopback 与外部 IP 的安全讨论;同时关注上游 AITER issue 的进展,以便及时清理技术债。

讨论亮点
  • 安全审查(depthfirst-app[bot]):指出 uniproc_executor.py 用 get_ip() 绑定外部网络 IP 而不是 get_loopback_ip(),单进程场景没有远端 worker 需要连接,却把无认证、无加密的 PyTorch TCPStore 暴露到网络上;multiproc_executor.py 已经正确使用 loopback。机器人建议将 get_ip() 换成 get_loopback_ip() 并更新 import。该建议在合并时未被采纳,head 版本仍为 get_ip()。
  • 实现方案调整(tjtanaa):review 中直接给出 aiter_requires_tcp_store() 的替代实现,建议从 import vllm.envs as envs 改为直接使用 rocm_aiter_ops.is_custom_all_reduce_enabled();后续 commit 67cc5c0ec267 采纳了这个方案,作为权威开关。
  • 上游跟踪(stefankoncarevic):已向 AITER 仓库提交 upstream issue(ROCm/aiter#4658),请求上游接受 FileStore,以便将来移除这个特判分支。

实现拆解

  1. 新增统一判定函数:在 vllm/utils/network_utils.py 中新增 aiter_requires_tcp_store(),延迟导入 vllm._aiter_ops.rocm_aiter_ops 并返回 rocm_aiter_ops.is_custom_all_reduce_enabled()。这样把“是否需要 TCP store”的判断收敛到单一函数,避免两个 executor 各自重复解析 AITER 配置。
  2. 修改单进程 executor:vllm/v1/executor/uniproc_executor.py 的 _distributed_args() 改为条件分支——若 aiter_requires_tcp_store() 为真,用 get_distributed_init_method(get_ip(), get_open_port()) 生成 TCP init method;否则维持 get_file_store_init_method()。影响单进程 executor 的 worker 分布式初始化路径。
  3. 修改多进程 executor:vllm/v1/executor/multiproc_executor.py 的 _init_executor() 做同样的条件分支,但使用 get_loopback_ip() 绑定回环地址(多进程场景 worker 都在本机),其余逻辑不变。
  4. 演进与配套:提交历史显示初版由 vllmellm 用 env 检查实现,随后 tjtanaa 在 review 中提出改为调用 rocm_aiter_ops 并提交 commit 67cc5c0;最后合入 main。本 PR 未新增测试文件;合并后有 jiacao-amd 在 MI355X TP=8 的 DeepSeek-V4-Pro 服务负载上做了端到端验证,确认 module_custom_all_reduce.so 在所有 worker 加载且 TCP rendezvous 生效。
文件 模块 状态 重要度
vllm/utils/network_utils.py 网络工具 modified 6.46
vllm/v1/executor/uniproc_executor.py 单进程执行器 modified 6.21
vllm/v1/executor/multiproc_executor.py 多进程执行器 modified 5.94

关键符号

aiter_requires_tcp_store UniProcExecutor._distributed_args MultiprocExecutor._init_executor

关键源码片段

vllm/utils/network_utils.py core-logic

新增核心判定函数 aiter_requires_tcp_store(),所有 executor 的 TCP/file 选择都收敛到这个开关,是本 PR 的逻辑中枢。

def aiter_requires_tcp_store() -> bool:
    """AITER custom all-reduce 要求默认 KV store 是纯 TCP 实现。
​
    AITER 的 IPC 元数据交换在 `custom_all_reduce.py` 中直接 assert 默认 store
    是 `torch.distributed.TCPStore`;而 `file://` rendezvous 产生的是 `FileStore`,
    会触发 `AssertionError`,导致 ROCm 服务启动即失败。
    因此在 AITER 上游接受 `FileStore`(ROCm/aiter#4658)之前,
    这里需要返回 True 让 executor 回退到 #50999 之前的 TCP rendezvous。
    """
    # 延迟导入,避免在非 ROCm 或未启用 AITER 的路径上引入额外依赖开销
    from vllm._aiter_ops import rocm_aiter_ops
​
    return rocm_aiter_ops.is_custom_all_reduce_enabled()
vllm/v1/executor/uniproc_executor.py dependency-wiring

单进程 executor 的分布式初始化入口被改动,决定 worker 使用 file:// 还是 TCP init method;同时是安全 review 关注的文件。

def _distributed_args(self) -> tuple[str, int, int]:
    """Return (distributed_init_method, rank, local_rank)."""
    if aiter_requires_tcp_store():
        # AITER custom all-reduce 只认 TCPStore,必须回退到 TCP rendezvous;
        # get_ip() + get_open_port() 会生成 `tcp://<ip>:<port>` 形式的 init method
        distributed_init_method = get_distributed_init_method(
            get_ip(), get_open_port()
        )
    else:
        # 默认路径维持 #50999 之后的 file:// 方案,避免端口竞争
        distributed_init_method = get_file_store_init_method()
    # set local rank as the device index if specified
    device_info = self.vllm_config.device_config.device.__str__().split(":")
    local_rank = int(device_info[1]) if len(device_info) > 1 else 0
    return distributed_init_method, 0, local_rank

评论区精华

uniproc 使用外部 IP 绑定 TCPStore 的安全隐患 安全

depthfirst-app[bot] 指出:单进程 executor 场景没有远端 worker 需要连接,却用 get_ip() 绑定外部网络 IP,暴露了无认证、无加密的 PyTorch TCPStore;而 multiproc_executor.py 已正确使用 get_loopback_ip()。项目 docs/usage/security.md 也提示 PyTorch distributed 会无授权接受任意连接。

结论:机器人建议将 uniproc 的 get_ip() 改为 get_loopback_ip(),但合并时 head 版本仍为 get_ip(),该建议未在本次 PR 内落实。 · 未解决

判定实现从 env 检查改为 rocm_aiter_ops 设计

tjtanaa 在 review 中直接给出 aiter_requires_tcp_store() 的替代实现,建议使用 rocm_aiter_ops.is_custom_all_reduce_enabled() 作为权威开关,而不是自己解析 env。

结论:提交 67cc5c0ec267 采纳了该方案,后续版本统一走 vllm._aiter_ops.rocm_aiter_ops。 · 已解决

上游 AITER issue 跟踪 other

stefankoncarevic 在 issue 评论中说明已向 AITER 仓库提交 upstream ask(ROCm/aiter#4658),以便将来移除这个特殊分支。

结论:已记录待跟进;在 AITER 接受 FileStore 前,本 PR 的特判需要保留。 · 待跟进

风险与影响

  1. 端口竞争风险回退:本 PR 在 ROCm + AITER 场景下恢复了 TCP 随机端口 rendezvous,这正是 #50999 想消除的启动端口竞争窗口;get_open_port() 的“先取端口后监听”存在 TOCTOU 窗口,极端并发下可能复现原问题。
  2. 安全暴露面:uniproc_executor.py 使用 get_ip() 绑定外部网络 IP 的 TCPStore,不符合项目 docs/usage/security.md 对 PyTorch distributed 无认证连接的风险提示;multiproc_executor.py 用 get_loopback_ip() 则无此问题。该不一致在 review 中被提出但未修改。
  3. 平台依赖与异常路径:aiter_requires_tcp_store() 内部直接 from vllm._aiter_ops import rocm_aiter_ops 并调用,若在非 ROCm 平台或 AITER 未安装的情况下该方法没有返回 False 的兜底,可能引入新的导入异常;当前验证覆盖了 ROCm 场景,但跨平台 CI 未覆盖该分支。
  4. 缺少单测:PR 没有新增针对 aiter_requires_tcp_store() 或两个 executor 分支的单元测试,后续回归主要依赖手工/集成验证。

影响范围限定在 ROCm + AITER custom all-reduce 启用 的用户配置:修复了这些用户每次启动 server 必现的 worker init 崩溃。默认 file:// 路径、非 ROCm 平台、未启用 AITER 的 ROCm 路径行为不变。对团队而言,这是一个需要跟踪上游的过渡补丁,待 ROCm/aiter#4658 落地后应删除 aiter_requires_tcp_store() 特判并恢复统一 file:// 方案。影响程度中等偏低,属于平台限定但阻塞性强的修复。

平台限定修复 回退引入端口竞争窗口 uniproc 绑定外部 IP 的安全暴露 缺少单元测试覆盖 依赖上游 AITER 修复

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论