Model Runner V2:vLLM 更模块化、更快速的核心

8 分钟阅读
vLLM 团队

我们很高兴宣布推出 Model Runner V2 (MRV2),这是对 vLLM 模型运行器(model runner)的重构。MRV2 提供了更整洁、更模块化且更高效的执行核心——并且完全兼容原有 API。我们的目标很简单:打造更好的代码和更高的性能。

与去年发布的 vLLM V1 一样,这是一次架构升级,源于 vLLM 庞大用户群的实战经验和社区反馈。我们重新审视了持久批处理、异步调度、输入准备和采样,并围绕三个核心原则重构了模型运行器:

  • 模块化: 将模型特定的逻辑与通用执行路径解耦。
  • GPU 原生: 将记录工作从 CPU 转移到 GPU。
  • 异步优先: 将重叠的 CPU/GPU 执行视为设计约束,而非事后补救。

MRV2 目前尚未实现所有功能,但您现在就可以尝试。

export VLLM_USE_V2_MODEL_RUNNER=1

我们计划在不久的将来将 MRV2 设置为默认选项。

为什么要推出 Model Runner V2?

自去年发布 vLLM V1 以来,随着功能和优化的不断增加,模型运行器积累了大量的技术债务。虽然许多变更在孤立场景下很有用,但随着异步调度和投机解码成为执行模型的核心,代码实现变得难以维护和理解。

在实际应用中,这导致了几个反复出现的问题:

  • 持久批处理状态混乱: 持久状态与每一步的模型输入高度耦合,使得请求的插入、移除和重排序变得异常复杂。
  • 脆弱的异步执行: 异步调度是后来添加到 V1 运行器中的,因此许多功能需要通过极其复杂且不自然的逻辑才能与之共存。
  • CPU 瓶颈: 输入准备和采样依赖于大量的小型 CPU 端操作,随着 GPU 处理能力的提升,这成为了性能的掣肘。
  • 难以扩展: 随着新模型和新功能的引入,整个运行器变得越来越难理解和扩展。

MRV2 通过更清晰的状态所有权和更显式的抽象设计,解决了上述问题。

Model Runner V2 有哪些新特性?

1. 改进的持久批处理设计与 GPU 原生输入准备

vLLM 需要为批处理、分页注意力(PagedAttention)、采样参数等执行大量记录工作。过去,这些工作大多被实现为 CPU 端的众多细碎操作。

为了减少开销,vLLM V1 引入了持久批处理:由于连续的批次通常非常相似,增量更新缓存状态远比每一步重新构建大型张量要经济。然而,V1 的设计直接将持久状态用作模型和采样器的输入,这导致了布局约束的限制并增加了记录工作的复杂性。

Figure 1: Persistent batch in V1. Request ordering is tightly coupled to the block table layout, requiring complex reordering when requests are added or removed.
图 1:V1 中的持久批处理。请求顺序与块表(block table)布局紧密耦合,导致在添加或移除请求时需要进行复杂的重排序。

MRV2 将持久化请求状态与每一步的输入张量解耦。每个活跃的请求在生命周期内都会在固定大小的状态表中占据一个稳定的行。在每一步中,运行器根据当前的请求顺序从该持久状态中收集步骤特定的输入。这既保留了增量更新的性能优势,又消除了大量的状态管理复杂性。它还消除了诸如 CachedRequestState 等冗余备份状态,因为活跃请求不再依赖于脆弱的张量重排序。

Figure 2: Persistent batch in MRV2. A stable state table is maintained independently of the per-step input layout. A gather operation produces the correctly ordered input block table each step.
图 2:MRV2 中的持久批处理。一个稳定的状态表与每步输入布局独立维护。收集(Gather)操作会在每一步产生排序正确的输入块表。

MRV2 还使用 Triton 内核将输入准备过程移至 GPU。请求状态主要保存在设备内存中,张量(如 input_idspositionsquery_start_locseq_lens)现在直接在 GPU 上构建。这带来了三个具体好处:

  • 降低 CPU 开销: 避免了大量的 Python 和 CPU 端张量操作。
  • 降低代码复杂度: 移除了 CPU 端张量操作所带来的限制。
  • 更好的异步和投机解码兼容性: 因为驻留在 GPU 上的准备工作可以直接使用设备端结果,无需同步(见下一节)。

2. 异步优先设计

异步调度现在是 vLLM 的基石。调度器和工作进程在 GPU 执行第 N 步时准备第 N+1 步,通过重叠主机端和设备端的工作来最大化利用率。虽然 vLLM V1 已支持此功能,但它更多是作为一种附加补丁而非核心设计约束。

Figure 3: Async scheduling in V1. The CPU schedules and prepares the next step while the GPU executes the current step, overlapping CPU and GPU work.
图 3:V1 中的异步调度。CPU 在 GPU 执行当前步骤时调度并准备下一步,实现 CPU 与 GPU 的工作重叠。

MRV2 将异步执行作为核心假设,并力求在所有支持的模型和功能组合中实现零同步

重要的是,MRV2 自然地支持异步调度与投机解码的协同工作——这在 V1 中很难优雅实现。由于 MRV2 的输入准备在设备上进行,准备内核可以直接使用 GPU 产生的拒绝采样(rejection sampling)结果。每一步的输出通过独立的 CUDA 流异步传输到 CPU,与主计算流完全解耦。同样的设计也适用于带有结构化输出的投机解码。

Figure 4: MRV2 async scheduling with speculative decoding. GPU-side prep kernels consume rejection sampling results directly, eliminating CPU–GPU sync points.
图 4:MRV2 中结合投机解码的异步调度。GPU 端准备内核直接消费拒绝采样结果,消除了 CPU 与 GPU 之间的同步点。

3. Triton 原生采样器

MRV2 通过优化的 Triton 内核重构了采样过程,从而更好地控制内存使用和数值精度。具体改进包括:

  • Gumbel-Max 采样内核: 避免了显式的 Softmax 具体化,并使用内核内的无状态 RNG。
  • 更高效的 top-k logprobs: 先找到 top-k logit,仅为选定的候选值计算对数概率。
  • 更节省内存的提示词 logprobs: 通过更细粒度的分块(包括单个提示词内部的分块)。
  • 更好的投机解码兼容性: 通过在内核内部使用间接寻址(idx_mapping),而不是扩展请求状态以匹配每个 logit 向量。

综合来看,这些改变降低了峰值内存使用率,并简化了对多种采样参数组合的支持。

4. 更强的模块化

vLLM 需要支持广泛的模型架构,原有的模型运行器因此积累了相当大的复杂性。MRV2 通过新的抽象概念解决了这个问题:ModelState

class ModelState(ABC):
    def add_request(self, ...):
    def remove_request(self, ...):
    def get_mm_embeddings(self, ...):
    def prepare_inputs(self, ...):
    def prepare_attn(self, ...):
    def prepare_dummy_inputs(self, ...):
    ...

ModelState 定义了模型特定逻辑的接口(多模态嵌入、额外模型输入、注意力元数据、CUDA 图捕获),从而使主运行器可以专注于通用路径。这直接回应了用户和贡献者的常见抱怨:vLLM 支持的模型太多,导致共享代码变得非常复杂,特别是对于那些只关心特定模型家族(如 DeepSeek、Qwen、Kimi 或内部私有模型)的开发者而言。

此外,MRV2 将运行器拆分为职责更清晰的小型文件。现有的运行器(gpu_model_runner.py)已增长到超过 6700 行的单一文件;而 MRV2 中最大的文件现在不到 1300 行。

性能

MRV2 不仅仅是一个清理项目,它已经带来了实实在在的性能提升。

我们对 MRV2 进行了压力测试:在强大的 GPU (1×GB200) 上运行一个极小的模型 (Qwen3-0.6B),故意选择小模型是为了使主机端的开销占比更大。在这种设置下,MRV2 通过将输入准备卸载到 GPU,实现了 56% 的吞吐量提升

Figure 5: Throughput comparison between MRV1 and MRV2 on Qwen3-0.6B with 1×GB200. MRV2 achieves 25K output tok/s vs 16K for MRV1, a 56.2% improvement.
图 5:在 1×GB200 上运行 Qwen3-0.6B 时,MRV1 与 MRV2 的吞吐量对比。MRV2 达到 25K 输出 token/s,而 MRV1 为 16K,提升了 56.2%。

我们还测量了投机解码的性能收益:在 4×GB200 上使用 GLM-4.7-FP8MTP=1TPOT 降低了 6.3%。这一提升归功于 MRV2 的零同步设计,它完全消除了启用投机解码时的 CPU–GPU 同步点。

Figure 6: Mean TPOT comparison between MRV1 and MRV2 on GLM-4.7-FP8 with MTP=1 on 4×GB200. MRV2 achieves 6.3% lower TPOT across request rates.
图 6:在 4×GB200 上使用 MTP=1 运行 GLM-4.7-FP8 时,MRV1 与 MRV2 的平均 TPOT 对比。在各种请求速率下,MRV2 的 TPOT 均降低了 6.3%。

我们预计,随着推理栈继续结合异步调度、投机解码、多模态预处理以及日益异构的模型状态,这种架构基础将显得更加重要。

局限性与当前状态

MRV2 仍处于实验阶段,正在积极开发中。其设计显著更整洁,且早期成果稳健,但功能尚不完整。截至 v0.18.0,以下功能尚不支持

  • 线性注意力模型(Qwen3.5, Nemotron 3 Super)
  • 除 Eagle/Eagle3/MTP 之外的投机解码方法
  • EPLB 和 DBO
  • Logits 处理器
  • LoRA

完整列表请参考设计文档的第二页。

我们对 MRV2 设定了更高的质量标准:当将 V1 功能引入 MRV2 时,我们倾向于从第一性原理重新考虑,而不是机械地复制复杂度。因此,涉及 MRV2 的变更可能需要比平时更长的时间来落地。

入门指南

  1. 安装最新版 vLLM。
  2. 设置 export VLLM_USE_V2_MODEL_RUNNER=1
  3. 正常使用现有的 vLLM API(Python API 或 vllm serve)。

无需进行任何用户端 API 变更。

致谢

Woosuk Kwon, Nick Hill, Giancarlo Delfin, Santino Ramos (Inferact), Wentao Ye, Zhanqiu Hu, Lucas Wilkinson (Red Hat), Haoran Zhu (Alibaba)