vLLM V1:vLLM 核心架构的一次重大升级
我们非常激动地宣布 vLLM V1 Alpha 版发布,这是 vLLM 核心架构的一次重大升级。基于过去一年半 vLLM 开发过程中积累的经验,我们重新审视了关键的设计决策,整合了各项功能,并简化了代码库,以增强灵活性和可扩展性。V1 已经实现了最先进的性能(SOTA),并计划进行更多优化。最重要的是,用户可以无缝启用 V1——只需设置 VLLM_USE_V1=1 环境变量,无需对现有 API 进行任何更改。在接下来的几周完成测试和反馈收集后,我们计划将 V1 转为默认引擎。
为什么要推出 vLLM V1?
从 vLLM V0 中学习
在过去的一年半里,vLLM 在支持多样化的模型、功能和硬件后端方面取得了卓越的成功。然而,当我们的社区在横向扩展时,我们在简化系统以及跨堆栈垂直集成各种优化方面面临着挑战。功能往往是独立开发的,导致难以有效地、整洁地将它们组合在一起。随着时间的推移,技术债不断累积,这促使我们重新审视基础设计。
V1 的目标
基于上述动机,vLLM V1 的设计目标是:
- 提供一个简单、模块化且易于修改(easy-to-hack)的代码库。
- 确保高性能,且 CPU 开销接近于零。
- 将关键优化组合进统一的架构中。
- 通过默认启用功能和优化来实现零配置。
V1 的范围
vLLM V1 对其核心组件进行了全面的架构重组,包括调度器、KV 缓存管理器、Worker、采样器和 API 服务器。尽管如此,它仍然与 vLLM V0 共享大量代码,例如模型实现、GPU 内核、分布式控制平面和各种实用函数。这种方法使 V1 能够利用 V0 建立的广泛覆盖范围和稳定性,同时在性能和代码复杂度方面提供显著提升。
vLLM V1 有哪些新变化?
1. 优化的执行循环与 API 服务器
作为一个成熟的连续批处理引擎和兼容 OpenAI 的 API 服务器,vLLM 的核心执行循环依赖 CPU 操作来管理模型前向传递之间的请求状态。随着 GPU 变得越来越快,模型执行时间显著缩短,运行 API 服务器、调度任务、准备输入、反标记化(de-tokenizing)输出以及向用户流式传输响应等任务的 CPU 开销变得日益突出。在使用 NVIDIA H100 GPU 运行 Llama-8B 等较小模型时,这一问题尤为明显,因为 GPU 上的执行时间仅为 ~5ms。
在 v0.6.0 版本中,vLLM 引入了利用 ZeroMQ 进行进程间通信(IPC)的多进程 API 服务器,实现了 API 服务器与 AsyncLLM 之间的重叠运行。vLLM V1 通过将多进程架构进一步深入集成到 AsyncLLM 核心中扩展了这一点,创建了一个隔离的 EngineCore 执行循环,专门负责调度器和模型执行器。这种设计允许 CPU 密集型任务(如分词、多模态输入处理、反标记化和请求流式传输)与核心执行循环实现更大程度的重叠,从而最大化模型吞吐量。
2. 简单且灵活的调度器
vLLM V1 引入了一个简单而灵活的调度器。它通过统一处理用户给定的 Prompt Token 和模型生成的 Output Token,消除了传统的“Prefill(预填充)”和“Decode(解码)”阶段之间的区别。调度决策表示为一个简单的字典,例如 {request_id: num_tokens},指定了每一步为每个请求处理的 Token 数量。我们发现这种表示法足以支持分块预填充(chunked prefills)、前缀缓存和投机采样等功能。例如,分块预填充调度可以无缝实现:在固定的 Token 预算下,调度器动态决定为每个请求分配多少 Token(如上图所示)。
3. 零开销前缀缓存
与 V0 一样,vLLM V1 使用基于哈希的前缀缓存和基于 LRU 的缓存置换。在 V0 中,启用前缀缓存有时会产生显著的 CPU 开销,导致在缓存命中率较低时性能反而下降。因此,它在 V0 中默认是禁用的。在 V1 中,我们针对常数时间(constant-time)的缓存置换优化了数据结构,并仔细最小化了 Python 对象的创建开销。这使得 V1 的前缀缓存即使在命中率为 0% 时,其导致的性能下降也几乎为零。
以下是一些基准测试结果。在我们的实验中,我们观察到即使缓存命中率为 0%,V1 的前缀缓存导致的吞吐量下降也不到 1%;而当命中率较高时,它能将性能提高数倍。得益于接近零的开销,我们现在在 V1 中默认启用前缀缓存。
4. 整洁的张量并行(Tensor-Parallel)推理架构
vLLM V1 为张量并行推理引入了整洁且高效的架构,有效地解决了 V0 的局限性。在 V0 中,调度器和 Worker 0 位于同一进程中,以减少向 Worker 广播输入数据时的进程间通信开销。然而,这种设计引入了不对称架构,增加了复杂性。V1 通过在 Worker 端缓存请求状态,并仅在每一步传输增量更新(diffs)来克服这一问题。这种优化最小化了进程间通信,允许调度器和 Worker 0 在独立进程中运行,从而实现了整洁、对称的架构。此外,V1 抽象掉了大部分分布式逻辑,使得 Worker 在单 GPU 和多 GPU 设置下的运行方式完全相同。
5. 高效的输入准备
在 vLLM V0 中,模型的输入张量和元数据在每一步都会重新创建,这往往导致显著的 CPU 开销。为了优化这一点,V1 实现了 Persistent Batch 技术,该技术会缓存输入张量,并仅在每一步对其应用增量更新。此外,V1 通过广泛使用 Numpy 操作而非 Python 原生操作,最小化了更新张量时的 CPU 开销。
6. torch.compile 与分段式 CUDA 图(Piecewise CUDA Graphs)
V1 利用 vLLM 的 torch.compile 集成来自动优化模型。这使得 V1 能够高效支持各种模型,同时最小化编写自定义内核的需求。此外,V1 引入了分段式 CUDA 图,以缓解传统 CUDA 图的局限性。我们正在准备关于 torch.compile 集成和分段式 CUDA 图的专门博客文章,敬请期待更多更新!
7. 增强的多模态 LLM 支持
vLLM V1 将多模态大语言模型(MLLM)视为一等公民,并在其支持方面引入了多项关键改进。
首先,V1 通过将多模态输入预处理移动到非阻塞进程中进行了优化。例如,图像文件(如 JPG 或 PNG)在输入模型前必须转换为像素值张量、进行裁剪和变换。这种预处理可能会消耗大量的 CPU 周期,甚至导致 GPU 闲置。为了解决这个问题,V1 将预处理任务卸载到单独的进程中,防止其阻塞 GPU Worker,并添加了预处理缓存,以便在请求共享相同的多模态输入时重用处理后的输入。
其次,V1 为多模态输入引入了前缀缓存。除了 Token ID 的哈希外,图像哈希也被用于识别图像输入的 KV 缓存。这一改进对于包含图像输入的后续多轮对话尤其有利。
第三,V1 通过“编码器缓存(encoder cache)”为 MLLM 启用了分块预填充调度。在 V0 中,图像输入和文本输入必须在同一步处理,因为 LLM 解码器的 Token 依赖于在步后会被丢弃的视觉嵌入。有了编码器缓存,V1 可以临时存储视觉嵌入,允许调度器将文本输入分成块并在多步中处理,而无需每步重新生成视觉嵌入。
8. FlashAttention 3
vLLM V1 的最后一块拼图是集成 FlashAttention 3。鉴于 V1 具有高度的动态性——例如在同一批次中组合预填充和解码——一个灵活且高性能的注意力机制内核至关重要。FlashAttention 3 有效地满足了这一要求,为广泛的功能提供了强有力的支持,同时在各种使用场景中保持了卓越的性能。
性能
得益于广泛的架构增强,vLLM V1 实现了最先进的吞吐量和延迟,与 V0 相比,吞吐量提升高达 1.7 倍(不包含多步调度)。这些显著的性能收益源于整个堆栈中 CPU 开销的全面降低。由于 V1 增强了对视觉语言模型(VLM)的支持,Qwen2-VL 等模型的提升甚至更为明显。
- 文本模型:Llama 3.1 8B & Llama 3.3 70B
我们使用 ShareGPT 数据集测量了 vLLM V0 和 V1 在 Llama 3.1 8B 和 Llama 3.3 70B 模型上的性能。得益于更高的吞吐量,V1 在高 QPS 下表现出比 V0 持续更低的延迟。考虑到 V0 和 V1 使用的内核几乎完全相同,性能差异主要源于 V1 的架构改进(减少了 CPU 开销)。
- 视觉语言模型:Qwen2-VL
我们通过使用 VisionArena 数据集测试 Qwen2-VL 来评估 VLM 的性能。得益于改进的 VLM 支持(由两项关键改进驱动:将输入处理卸载到独立进程,以及为多模态查询实现更灵活的调度),V1 相比 V0 实现了更大的加速。我们还想指出,V1 现在原生支持多模态模型的前缀缓存,但此处省略基准测试结果。
- 展望未来
虽然这些改进意义重大,但我们认为这仅仅是个开始。重新设计的架构提供了一个坚实的基础,将推动新功能的快速开发。我们期待在未来几周分享更多增强功能。请关注更多更新!
局限性与后续工作
尽管 vLLM V1 展现了令人鼓舞的结果,但它仍处于 Alpha 阶段,且缺少一些 V0 的功能。以下是具体说明:
模型支持
V1 支持仅解码器(Decoder-only)架构的 Transformer 模型(如 Llama)、混合专家(MoE)模型(如 Mixtral)以及 Qwen2-VL 等几种 VLM。支持所有量化方法。然而,V1 目前不支持编码器-解码器(Encoder-decoder)架构(如多模态 Llama 3.2)、基于 Mamba 的模型(如 Jamba)或嵌入模型(Embedding models)。请查阅我们的文档以获取更详细的支持模型列表。
功能限制
V1 目前尚不支持 log probs、prompt log probs 采样参数、流水线并行(Pipeline parallelism)、结构化解码(Structured decoding)、投机采样(Speculative decoding)、Prometheus 指标和 LoRA。我们正在积极努力填补这些功能空白,并为 V1 引擎添加全新的优化。
硬件支持
V1 目前仅支持 Ampere 或更高版本的 NVIDIA GPU。我们正在积极工作,将支持扩展到 TPU 等其他硬件后端。
最后,请注意,如果不设置 VLLM_USE_V1=1,您可以继续使用 V0 并保持向后兼容性。
如何开始
要使用 vLLM V1:
- 使用
pip install vllm --upgrade安装最新版本的 vLLM。 - 设置环境变量
export VLLM_USE_V1=1。 - 使用 vLLM 的 Python API 或兼容 OpenAI 的服务器(
vllm serve <model-name>)。您无需对现有 API 进行任何更改。
请试用并分享您的反馈!
致谢
我们诚挚地感谢 vLLM V1 的设计参考并增强了多个开源 LLM 推理引擎,包括 LightLLM、LMDeploy、SGLang、TGI 和 TRT-LLM。这些引擎极大地影响了我们的工作,我们从中获得了宝贵的见解。
V1 架构重组是整个 vLLM 团队和社区持续共同努力的结果。以下是这一里程碑的部分贡献者名单:
- 加州大学伯克利分校(UC Berkeley)、Neural Magic(现属于红帽)、Anyscale 和 Roblox 主要共同推动了这一工作。
- Woosuk Kwon 发起了该项目并实现了调度器和模型运行器(Model Runner)。
- Robert Shaw 实现了优化的执行循环和 API 服务器。
- Cody Yu 为文本和图像输入实现了高效的前缀缓存。
- Roger Wang 领导了 V1 中多模态 LLM 支持的全面增强。
- Kaichao You 领导了 torch.compile 的集成并实现了分段式 CUDA 图。
- Tyler Michael Smith 实现了基于 Python 多进程的张量并行支持。
- Rui Qiao 实现了基于 Ray 的张量并行支持,并正在实现流水线并行支持。
- Lucas Wilkinson 添加了对 FlashAttention 3 的支持。
- Alexander Matveev 为多模态输入实现了优化的预处理器,并正在实现 TPU 支持。
- Sourashis Roy 在采样器中实现了 Logit Penalty(Logit 惩罚)。
- Cyrus Leung 领导了 MLLM 输入处理的重构工作,并协助其集成到 V1。
- Russell Bryant 解决了多个与多进程相关的问题。
- Nick Hill 优化了引擎循环和 API 服务器。
- Ricky Xu 和 Chen Zhang 协助重构了 KV 缓存管理器。
- Jie Li 和 Michael Goin 在 MLLM 支持和优化方面提供了帮助。
- Aaron Pham 正在实现结构化解码支持。
- Varun Sundar Rabindranath 正在实现多 LoRA 支持。
- Andrew Feldman 正在实现 log probs 和 prompt log probs 支持。
- Lily Liu 正在实现投机采样支持。
- Kuntai Du 正在实现预填充解耦(Prefill Disaggregation)和 KV 缓存传输支持。
- Simon Mo 和 Zhuohan Li 对 V1 的系统设计做出了贡献。