在 vLLM Ascend 中的补丁

在 vLLM Ascend 中的补丁#

vLLM Ascend 是 vLLM 的一个平台插件。由于 vLLM 和 vLLM Ascend 的发布周期不同,并且在某些情况下存在硬件限制,我们需要对 vLLM 进行一些代码补丁,以使其能够兼容 vLLM Ascend。

在 vLLM Ascend 代码中,我们提供了一个补丁模块 vllm_ascend/patch 用于应对 vLLM 的变更。

原理#

我们需要记住,Patch 不是让 vLLM 兼容 Ascend 的最佳方式,这只是一个临时的解决方案。最好的方法是将修改贡献到 vLLM 项目中,从而让 vLLM 原生支持 Ascend。对于 vLLM Ascend,我们对 Patch 策略有一个基本原则:

  1. 少即是多。请不要打补丁,除非这是目前唯一的方法。

  2. 一旦补丁被添加,必须说明将来移除该补丁的计划。

  3. 任何时候,欢迎清理补丁代码。

工作原理#

vllm_ascend/patch 目录中,你可以看到如下代码结构:

vllm_ascend
├── patch
│   ├── platform
│   │   ├── patch_0_9_2
│   │   ├── patch_common
│   │   ├── patch_main
│   ├── worker
│   │   ├── patch_0_9_2
│   │   ├── patch_common
│   │   ├── patch_main
└───────────
  • platform:此目录下的补丁代码用于修补 vLLM 主进程中的代码。当 vLLM 初始化时,会在很早的阶段由 vllm_ascend/platform::NPUPlatform::pre_register_and_update 调用。

    • 对于在线模式,vLLM 进程在解析命令行参数时,会在 vllm/vllm/engine/arg_utils.py::AsyncEngineArgs.add_cli_args 这里调用平台补丁。

    • 对于离线模式,vLLM 进程在解析输入参数时,会在此处调用平台补丁 vllm/vllm/engine/arg_utils.py::EngineArgs.create_engine_config

  • worker:此目录中的补丁代码用于修补 vLLM worker 进程中的代码。在初始化 vLLM worker 进程时,会被 vllm_ascend/worker/worker_v1::NPUWorker::__init__ 调用。

    • 无论是在线还是离线模式,vLLM 引擎核心进程在初始化 worker 进程时,都会在这里调用 worker 补丁:vllm/vllm/worker/worker_base.py::WorkerWrapperBase.init_worker

platformworker 文件夹中都有一些补丁模块。它们用于修补不同版本的 vLLM。

  • patch_0_9_2:此模块用于修补 vLLM 0.9.2。该版本始终对应于 vLLM 的最近版本。一旦 vLLM 发布新版本,我们将移除此补丁模块并升级到新版本。例如,patch_0_9_2 就是用于修补 vLLM 0.9.2 的。

  • patch_main:该模块用于修补 vLLM 主分支代码。

  • patch_common:此模块用于同时修补 vLLM 0.9.2 版本和 vLLM 主分支。

如何撰写补丁#

在编写补丁之前,遵循上述原则,我们应尽量修改最少的代码。如果有必要,我们可以修改 platformworker 文件夹中的代码。下面是一个在 vLLM 中修改 distributed 模块的示例。

  1. 决定我们应该修补哪个版本的 vLLM。例如,经过分析后,这里我们想要同时修补 vLLM 的 0.9.2 版和主分支(main)。

  2. 决定我们应该修补哪个进程。例如,这里 distributed 属于 vLLM 主进程,所以我们应该修补 platform

  3. 在正确的文件夹中创建补丁文件。文件应命名为 patch_{module_name}.py。此处的示例是 vllm_ascend/patch/platform/patch_common/patch_distributed.py

  4. 在新文件中编写你的补丁代码。以下是一个示例:

    import vllm
    
    def patch_destroy_model_parallel():
        # your patch code
        ...
    
    vllm.distributed.parallel_state.destroy_model_parallel = patch_destroy_model_parallel
    
  5. __init__.py 中导入补丁文件。在这个示例中,将 import vllm_ascend.patch.platform.patch_common.patch_distributed 添加到 vllm_ascend/patch/platform/patch_common/__init__.py 中。

  6. vllm_ascend/patch/__init__.py 中添加补丁的描述。描述格式如下:

    # ** File: <The patch file name> **
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #   1. `<The target patch module in vLLM>`
    #    Why:
    #       <Describe the reason why we need to patch>
    #    How:
    #       <Describe the way to patch>
    #    Related PR (if no, explain why):
    #       <Add a link to the related PR in vLLM. If there is no related PR, explain why>
    #    Future Plan:
    #       <Describe the future plan to remove the patch>
    
  7. 添加单元测试和端到端(E2E)测试。在 vLLM Ascend 中新增的任何代码也应包含单元测试和端到端测试。更多详情请参见 测试指南

限制#

  1. 在 V1 引擎中,vLLM 会启动三种类型的进程:主进程、EngineCore 进程和 Worker 进程。现在 vLLM Ascend 默认只支持在主进程和 Worker 进程中打补丁代码。如果你想要在 EngineCore 进程中打补丁,你需要在设置阶段对 EngineCore 进程整体打补丁,入口代码在 vllm.v1.engine.core。请完全重写 EngineCoreProcDPEngineCoreProc

  2. 如果你运行的是经过编辑的 vLLM 代码,vLLM 的版本可能会被自动更改。例如,如果你基于 v0.9.n 运行了编辑后的 vLLM,vLLM 的版本可能会变为 v0.9.nxxx,在这种情况下,vLLM Ascend 的 v0.9.n 补丁将无法正常工作,因为 vLLM Ascend 无法区分你所使用的 vLLM 版本。这时,你可以设置环境变量 VLLM_VERSION 来指定你所使用的 vLLM 版本,这样对 v0.9.2 的补丁就应该可以正常工作。