Prhub

#1903 Rename exec_command by the resource its command needs

原始 PR 作者 fzyzcjy 合并时间 2026-08-09 18:43 文件变更 120 提交数 10 评论 1 代码增减 +370 / -322

执行摘要

按资源语义拆分 exec_command 为 gpu/cpu 并全仓重命名

PR body 仅标注 "Part of #1837",结合 commit message "Rename exec_command by the resource its command needs" 可知:原 exec_command 名称无法表达命令所需资源类型(GPU 或 CPU),在大量 launch 脚本、示例和调试 CLI 中混用会导致资源使用意图不清晰,也不利于未来对 GPU 命令做特殊处理(如 CUDA_VISIBLE_DEVICES 管理)。同时 exec_command_all_ray_node 的 "all" 具有误导性——函数本身支持 num_nodes 限制,改名为 exec_command_multi_node 更准确。

值得精读 miles/utils/misc.py 的拆分方式与 test_shell_script_hygiene.py 中针对 docker patch 的防回退测试——这是大型机械重命名配合回归防护的典型范例。同时建议关注后续 #1904(将 helper 迁到唯一消费方)以及 #1905/#1909/#1911 等同一系列的演进,理解这次 rename 在整个 launch 脚本可复现性重构中的位置。

讨论亮点

该 PR 没有实质性的 review 讨论线程,review 评论为空;仅 reviewer yueming-yuan 给予 APPROVED。唯一的一条评论来自 gemini-code-assist[bot],内容是声明其代码评审活动已停止,不构成技术讨论。

实现拆解

  1. 核心拆分:在 miles/utils/misc.py 中,将原 exec_command 实现改名为私有 _exec_command,新增 exec_command_gpuexec_command_cpu 两个公共包装,全部委托 _exec_command_exec_command_on_node 内部调用同步改为 _exec_commandexec_command_all_ray_node 重命名为 exec_command_multi_node,函数体不变。
  2. 全仓调用点语义化替换miles/utils/external_utils/command_utils.pyhf downloadpkillray startmooncake_master 等归 exec_command_cpuconvert_checkpointfp8_cast_bf16nvidia-smi 等归 exec_command_gpursync_simpleexec_command_multi_nodemiles/utils/debug_utils/run_megatron/cli/commands/run.pyrun_implexec_command_gpushow_model_argsexec_command_cpuscripts/*.pyexamples/*.py 中目录创建、下载归 CPU,量化转换、模型转换等计算归 GPU。
  3. 测试配套tests/fast/utils/command_recorder.py 的 fake 函数同步改名(fake_exec_command_all_ray_nodefake_exec_command_multi_node);tests/fast/launch_scripts/test_shell_script_hygiene.py 新增 TestDockerPatchHygiene,用正则 (?<![\w.])exec_command\s*\( 扫描 docker/*.patch,防止镜像构建时引用已删除的旧 helper。
  4. 快照重录:受命令名影响的部分 launch 脚本快照重新生成,保证 CI 快照测试与 rename 后输出一致。
文件 模块 状态 重要度
miles/utils/misc.py 命令执行 modified 7.26
tests/fast/launch_scripts/test_shell_script_hygiene.py 脚本卫生 modified 6.58
miles/utils/external_utils/command_utils.py 命令工具 modified 6.22
miles/utils/debug_utils/run_megatron/cli/commands/run.py 运行 CLI modified 5.35
scripts/run_qwen3_30b_a3b.py 启动脚本 modified 5.28
tests/fast/utils/command_recorder.py 测试夹具 modified 5.1
examples/experimental/formal_math/single_round/kimina_wrapper.py 示例封装 modified 4.79
scripts/run_deepseek_v32.py 启动脚本 modified 5.17

关键符号

exec_command_gpu exec_command_cpu _exec_command exec_command_multi_node

关键源码片段

miles/utils/misc.py core-logic

核心变更文件:将 exec_command 拆分为 gpu/cpu 两个语义化入口并引入私有 _exec_command,同时重命名多节点执行函数,是本次重构的源头。

# 统一实现:真正执行 bash -c 命令的逻辑放在私有函数中,
# 公共入口只负责表达“命令需要哪种资源”,便于后续按资源差异化处理。
def _exec_command(cmd: str, capture_output: bool = False) -> str | None:
    print(f"EXEC: {cmd}", flush=True)
    try:
        result = subprocess.run(
            ["bash", "-c", cmd],
            shell=False,
            check=True,
            capture_output=capture_output,
            **(dict(text=True) if capture_output else {}),
        )
    except subprocess.CalledProcessError as e:
        if capture_output:
            print(f"{e.stdout=} {e.stderr=}")
        raise
    if capture_output:
        print(f"Captured stdout={result.stdout} stderr={result.stderr}")
        return result.stdout
    return None
​
​
# 面向 GPU 的命令:训练、推理、量化转换等;
# 面向 CPU 的命令:下载数据集、进程清理、启动 Ray、Docker 操作等。
def exec_command_gpu(cmd: str, capture_output: bool = False) -> str | None:
    return _exec_command(cmd, capture_output=capture_output)
​
​
def exec_command_cpu(cmd: str, capture_output: bool = False) -> str | None:
    return _exec_command(cmd, capture_output=capture_output)
miles/utils/external_utils/command_utils.py dependency-wiring

最大的调用方之一,展示了按资源语义划分调用点的典型模式:下载 / 清理归 cpu,转换 / 训练归 gpu,多节点同步归 multi_node。

# 单机模型转换需要 GPU 计算,因此走 exec_command_gpu;
# 多节点时使用 exec_command_multi_node,并透传 num_nodes 限制。
def convert_checkpoint(
    model_name,
    megatron_model_type,
    num_gpus_per_node: int,
    multinode: bool = False,
    num_nodes: int | None = None,
    extra_args: str = "",
    dir_dst: str = "/root",
    hf_checkpoint: str | None = None,
    megatron_path: str = "/root/Megatron-LM",
):
    # ...
    if multinode:
        fn = partial(exec_command_multi_node, num_nodes=num_nodes)
    else:
        fn = exec_command_gpu
    fn(
        f"source {repo_base_dir}/scripts/models/{megatron_model_type}.sh && "
        f"PYTHONPATH={pythonpath} "
        f"torchrun --nproc-per-node {num_gpus_per_node} "
        f"{multinode_args}"
        f"{repo_base_dir}/tools/convert_hf_to_torch_dist.py "
        "${MODEL_ARGS[@]} "
        f"--hf-checkpoint {hf_checkpoint} "
        f"--save {path_dst} "
        f"{extra_args}"
    )
​
​
# 进程清理、Ray 启动等准备工作是 CPU 操作,统一走 exec_command_cpu
def execute_train(
    train_args: str,
    num_gpus_per_node: int,
    megatron_model_type: str | None,
    train_script: str = "train.py",
    before_ray_job_submit=None,
    extra_env_vars=None,
    config: ExecuteTrainConfig | None = None,
    megatron_path: str = "/root/Megatron-LM",
):
    # ...
    exec_command_cpu(
        "pkill -9 sglang; "
        "sleep 3; "
        f"{'' if external_ray else 'ray stop --force; '}"
        f"{'' if external_ray else 'pkill -9 ray; '}"
        "pkill -9 miles; "
        "sleep 3; "
        "pkill -9 miles; "
        "pkill -9 redis; "
        "true; "
    )

评论区精华

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

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

风险与影响

  1. 机械替换遗漏风险:涉及 120 个文件,纯靠调用点手工替换,若有动态调用(如 getattr(U, ...))或不规则 import 可能遗漏,导致运行时 ImportError;当前 hygiene 测试只覆盖 docker/*.patch,没有对 Python 源码做防回退扫描。
  2. 外部兼容性exec_command 被删除后,任何仓库外的插件或 fork 若仍直接 from miles.utils.misc import exec_command 将立即断裂,属于跨模块 API 变更。
  3. 语义分化隐患:目前 exec_command_gpuexec_command_cpu 实现完全相同,资源差异完全由调用点约定保证;未来若在 GPU 版本中加入 CUDA_VISIBLE_DEVICES 等资源预处理,依赖隐式语义的调用点可能产生意外行为变化。
  4. _exec_command 私有性:作为私有函数却被模块内 _exec_command_on_node 引用,若后续迁移 helper(见 #1904)时误删会影响多节点执行路径。

影响范围覆盖全部 launch 脚本(scripts/)、示例(examples/)、command_utils 公共工具、run_megatron 调试 CLI 以及相关测试基础设施。对最终用户无功能影响,但要求团队在新代码中按资源语义选择 exec_command_gpu/exec_command_cpu/exec_command_multi_node;对维护者而言,删除旧名 exec_command 是一次跨模块的 API 清理,后续 #1904 将在此基础上把 shell exec helpers 迁出 misc.py

全仓 API 重命名 机械替换易遗漏 工具函数行为无变化 新增防回退测试

关联 Issue

#1837 Tracking issue for refactoring and enhancements

完整报告

参与讨论