Model Runner V2:vLLM 更模块化、更快速的核心
我们非常高兴地宣布 Model Runner V2 (MRV2),这是对 vLLM 模型运行器的彻底重构。MRV2 提供了一个更简洁、更模块化且更高效的执行核心——并且没有 API 变更。目标很简单:更好的代码,更好的性能。
与去年发布的 vLLM V1 一样,这是一个架构升级,得益于我们从庞大的 vLLM 用户群体中汲取的经验以及社区的反馈。我们重新审视了持久化批处理、异步调度、输入准备和采样流程,并围绕三个核心原则重构了模型运行器:
- 模块化:将模型特定的逻辑与通用执行路径隔离。
- GPU 原生:将簿记(bookkeeping)工作从 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 的设计直接将持久状态用作模型和采样器的输入,这导致了尴尬的布局约束和复杂的簿记。

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

MRV2 还使用 Triton 内核将输入准备工作转移到 GPU 上。请求状态主要保留在设备端,而 input_ids、positions、query_start_loc 和 seq_lens 等张量现在直接在 GPU 上构建。这提供了三个具体优势:
- 降低 CPU 开销:避免了大量 Python 和 CPU 端张量操作。
- 降低代码复杂度:通过消除 CPU 端张量操作的限制。
- 更好的异步和投机解码兼容性:因为驻留在 GPU 的准备工作可以直接消费设备端结果而无需同步(见下一节)。
2. 异步优先设计
异步调度现在是 vLLM 的基础。调度器和工作线程在 GPU 执行第 N 步时准备第 N+1 步,通过重叠主机端和设备端的工作来最大化利用率。虽然这在 vLLM V1 中已经得到支持,但它在很大程度上是一个补丁,而非一等设计原则。

MRV2 将异步执行视为一个核心假设,并旨在实现所有支持的模型和功能组合下零同步。
重要的是,MRV2 自然地将异步调度和投机解码结合在一起——这在 V1 中很难干净地实现。由于 MRV2 的输入准备在设备端运行,准备内核可以直接消费 GPU 产生的拒绝采样(rejection sampling)结果。每一步的输出都在独立的 CUDA 流中异步传输到 CPU,完全脱离了主计算流。同样的设计也扩展到了具有结构化输出的投机解码中。

3. 原生 Triton 采样器
MRV2 使用优化的 Triton 内核重构了采样流程,以更好地控制内存使用和数值计算。具体改进包括:
- Gumbel-Max 采样内核:避免了显式的 softmax 具体化(materialization),并使用无状态的内核内随机数生成器(RNG)。
- 更高效的 top-k 对数概率(logprobs):通过先找出 top-k logit,仅针对选定的候选者计算对数概率。
- 更高效的提示词对数概率内存使用:通过更细粒度的分块,包括在单个提示词内部的分块。
- 更好的投机解码兼容性:在内核内部使用间接寻址(
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)已增长到单个文件超过 6,700 行;MRV2 中最大的文件现在不到 1,300 行。
性能
MRV2 不仅仅是一个清理项目,它已经带来了可衡量的提升。
我们通过在一个强大的 GPU(1×GB200)上运行一个非常小的模型(Qwen3-0.6B)对 MRV2 进行了压力测试,故意选择小模型是为了使主机端开销在比例上显得更大。在这种设置下,MRV2 通过将输入准备工作卸载到 GPU,实现了 56% 的吞吐量提升。

我们还测量了投机解码的收益:在 4×GB200 上使用 GLM-4.7-FP8 和 MTP=1 时,TPOT(每个输出 token 的平均时间)降低了 6.3%。这种改进得益于 MRV2 的零同步设计,它在启用投机解码时完全消除了 CPU–GPU 同步点。

我们预计,随着服务栈继续结合异步调度、投机解码、多模态预处理以及日益异构的模型状态,这种架构基础将变得愈发重要。
局限性与当前状态
MRV2 仍处于实验阶段,并正在积极开发中。该设计明显更简洁,初步结果也很强劲,但 MRV2 尚未完全实现所有功能。截至 v0.18.0,以下功能暂不支持:
- 线性注意力模型 (Qwen3.5, Nemotron 3 Super)
- 除 Eagle/Eagle3/MTP 以外的投机解码方法
- EPLB 和 DBO
- Logits 处理器
- LoRA
完整列表请参考设计文档的第二页。
我们对 MRV2 设定了更高的质量标准:当我们将一个 V1 功能引入 MRV2 时,我们希望从第一性原理出发重新审视它,而不是机械地复制其复杂性。因此,涉及 MRV2 的变更落地可能需要比平时更长的时间。
入门指南
- 安装最新的 vLLM 构建版本。
- 设置
export VLLM_USE_V2_MODEL_RUNNER=1。 - 像往常一样使用现有的 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)