Prhub

#55468 [Fast Start] Fast loader support nnode>1

原始 PR 作者 liusy58 合并时间 2026-09-14 11:05 文件变更 1 提交数 9 评论 13 代码增减 +72 / -14

执行摘要

快速加载守护进程支持多节点 TP 部署

PR body 明确写明目的:Extend the fast loader to support multi-node tensor-parallel deployments through per-node daemons. 此前 weight cache daemon 只在单节点内用 127.0.0.1 临时端口初始化 TP 组,多节点部署下无法形成跨节点全局 TP 组,导致 multi-node TP 用户无法享受 fast restart(ipc_cache)的快速启动收益。

值得精读。关注三个设计决策:daemon 持有独立 rendezvous 端口的原因(跨节点组长期占用)、CUDA IPC 句柄节点本地性驱动的 per-node launcher 形态、以及全局/本地 rank 分离与引擎 rank 分配的一致性。建议同时结合 weight_cache 的 protocol.py 与 ipc_loader.py 阅读,理解 IPC 句柄导出与回放契约;注意该 PR 的 fp4 部分被拆出,合入内容仅为 nnode 支持。

讨论亮点
  1. coderabbit 对 modelopt.py 的 supports_pre_processed_weights 评论(针对早期 NVFP4 IPC 支持):supports_pre_processed_weights = True 无条件声明与实际选中的后端脱节——W4A16 会选 Marlin,但 _setup_kernel_from_pre_processed 只接受 FLASHINFER_TRTLLM,且 MarlinNvFp4LinearKernel 未设 ipc_pre_processed_safe,因此仍会走到 pre-processed loader 并抛 RuntimeError;建议从 select_nvfp4_moe_backend 与 self.kernel.ipc_pre_processed_safe 派生该能力。

  2. coderabbit 对 daemon.py 多节点 rendezvous 的稳定性意见:建议保留独立的 weight-cache rendezvous URI,验证端口在 1–65535 且与引擎 rendezvous 端口不同;最终版本采纳 Isotr0py 的简化写法(多节点缺端口直接报错、单节点自动选空闲端口),端口范围冲突校验未见显式处理。

  3. Isotr0py 指出 NodeRank/TP 规模校验冗余:0 <= node_rank < nnodes 与 tp_size % nnodes != 0 已由 ParallelConfig post-init 处理(parallel.py L485-L486),作者确认后删除,最终 head 不再包含这两段校验。

  4. Isotr0py 提问 ipc_pre_processed_safe 是否冗余:既然已有 supports_pre_processed_weights,是否还需要内核级 ipc_pre_processed_safe 标记;该问题停留在早期 fp4 讨论,未在当前合并范围解决。

实现拆解

  1. 梳理并行配置与节点拓扑:在 vllm/model_executor/model_loader/weight_cache/daemon.py 的 main() 中,从 vllm_config.parallel_config 读取 nnodes、node_rank、master_addr,计算 local_world_size = tp_size // nnodes。--nnodes、--node-rank、--master-addr 直接复用引擎启动参数,保证 daemon 与引擎的全局 TP 拓扑一致。多节点时必须额外指定 --weight-cache-master-port,因为 daemon 需要持有自己的 TP 组直到服务结束,不能与引擎的 --master-port 共用;单节点时该端口自动选取,保持向后兼容。

  2. 区分全局 rank 与本地 rank:WeightCacheDaemon.__init__ 与 _run_daemon 新增 local_rank 入参;load_model() 中用 torch.accelerator.set_device_index(self.local_rank) 绑定设备,init_distributed_environment(world_size=tp_size, rank=self.tp_rank, local_rank=self.local_rank) 参与跨节点组。全局 rank 由 node_rank * local_world_size + local_rank 计算,正好对应该节点 GPU 在 vLLM 连续 rank 分配中的位置。

  3. 按节点编排 daemon 进程:main() 不再为全部 tp_size 个 rank 启动进程,而是只启动本节点的 local_world_size 个;进程名使用全局 rank,就绪等待集合改为 local_world_size,任一进程提前退出会终止整组并退出。就绪日志改为节点级 node_rank/nnodes,并改用 logger.info_once 避免多进程重复输出。

  4. 文档与校验配套:模块 docstring 增加 2 节点 16 TP 的启动示例,说明每个节点各跑一个 launcher;--weight-cache-master-port 帮助文本明确端口必须跨节点一致、且与引擎端口不同。作者按 review 建议删除了 0 <= node_rank < nnodes 与 tp_size % nnodes != 0 的重复校验,因为 EngineArgs.create_engine_config() / ParallelConfig.post_init 已经覆盖。

  5. 测试配套:本 PR 未新增测试文件;多节点路径依赖手工或 CI 集成验证,单节点回归由既有 ipc_cache 用法保证(该路径逻辑基本不变)。

文件 模块 状态 重要度
vllm/model_executor/model_loader/weight_cache/daemon.py 权重缓存 modified 7.3

关键符号

WeightCacheDaemon.__init__ WeightCacheDaemon.load_model _run_daemon main

分析完成后,这里会展示 LLM 生成的相对完整源码片段和详细注释。

评论区精华

supports_pre_processed_weights 与后端能力脱节 正确性

coderabbit 指出 `supports_pre_processed_weights = True` 无条件声明会导致 W4A16/Marlin 路径仍走 pre-processed loader 并抛 RuntimeError,建议从 `select_nvfp4_moe_backend` 与 `self.kernel.ipc_pre_processed_safe` 派生。

结论:该文件未进入最终合并版本(PR 收敛为 daemon.py 的 nnode 支持),问题未在当前 PR 解决。 · 未解决(范围外)

多节点 rendezvous 端口的独立性与校验 正确性

coderabbit 建议 daemon 保持专用 weight-cache rendezvous URI,并验证端口在 1–65535 且与引擎端口不同;后续 Isotr0py 提出简化写法。

结论:最终采用 `nnodes > 1 and port is None` 时报错、单节点自动选端口的方案;端口范围与冲突校验未见显式处理。 · 部分解决

modelopt_fp4 allowlist 需排除不安全的 NVFP4 后端 正确性

coderabbit 指出 `protocol.py` 的 `IPC_QUANT_ALLOWLIST` 对 `modelopt_fp4` 一律放行,应拒绝 W4A16_NVFP4 等后端不经 IPC 后处理安全的配置。

结论:属于早期 fp4 部分,未在当前合并 diff 中体现。 · 未解决(范围外)

ipc_pre_processed_safe 是否冗余 设计

Isotr0py 提问:既然已有 `supports_pre_processed_weights`,是否还需要内核级 `ipc_pre_processed_safe` 标记。

结论:停留在 fp4 相关讨论,未进入当前合并范围。 · 未解决(范围外)

简化端口参数处理 style

Isotr0py 建议用 `master_port = args.weight_cache_master_port or get_open_port()` 合并多节点与单节点分支。

结论:作者采纳,最终代码即该写法。 · 已解决

重复校验应删除(ParallelConfig 已覆盖) 设计

Isotr0py 指出 `0 <= node_rank < nnodes` 与 `tp_size % nnodes != 0` 已由 `ParallelConfig` post-init 校验(parallel.py L485-L486),作者确认后删除。

结论:已解决,最终 head 不再包含这两段校验。 · 已解决

风险与影响

  1. 多节点 rendezvous 端口依赖:所有节点必须传相同 --weight-cache-master-port 且不能等于引擎 --master-port,否则 daemon 的 TP 组无法形成或端口被占用;main() 中只做了缺失检查,未校验端口取值 1–65535 以及与引擎端口的冲突。

  2. CUDA IPC 节点本地性假设:引擎 worker 只能从本节点 daemon 映射句柄;实现假设每节点 GPU 数一致(local_world_size = tp_size // nnodes),若实际节点 GPU 数不均匀或 rank 映射与引擎不一致,会出现跨节点映射错误,只有在运行时才会暴露。

  3. 无新增测试覆盖:多节点启动、就绪等待、进程早退退出路径均无单测,回归风险落在手工部署验证上,一旦引擎侧 rank 分配策略变化,这里很容易静默失配。

  4. fp4 相关内容未纳入合并:提交历史中的 support fp4 及相关 review(modelopt.py、protocol.py)没有出现在最终 diff 中,若用户组合 NVFP4 量化与 ipc_cache 多节点,可能仍停留在旧状态。

让 fast restart(--load-format ipc_cache)从单节点扩展到多节点 TP 部署,多节点用户可显著缩短引擎重启时间;每节点一个 launcher 的运维形态与引擎启动参数保持一致(复用 --nnodes/--node-rank/--master-addr),降低上手成本;对单节点用户无行为变化。对团队而言,weight cache daemon 的部署矩阵扩大,后续如需支持 pipeline/data parallelism 或非均匀节点拓扑,需要基于本次的 local/global rank 分离继续演进。

多节点 rendezvous 端口依赖 无测试覆盖 CUDA IPC 节点本地性假设 fp4 相关改动未纳入合并

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论