vLLM v0.6.0:吞吐量提升 2.7 倍,延迟降低 5 倍
简而言之:vLLM 在 Llama 8B 模型上实现了 2.7 倍的吞吐量提升和 5 倍的 TPOT(每个输出 token 的时间)优化;在 Llama 70B 模型上实现了 1.8 倍的吞吐量提升和 2 倍的 TPOT 优化。


vLLM v0.5.3 与 v0.6.0 在 ShareGPT 数据集(500 条提示词)上,针对 1xH100 运行 Llama 8B 和 4xH100 运行 70B 的性能对比。TPOT 在 32 QPS 下测量。
一个月前,我们发布了性能路线图,承诺将性能提升作为我们的首要任务。今天,我们发布了 vLLM v0.6.0,与 v0.5.3 相比,吞吐量提升了 1.8-2.7 倍,在保持丰富功能和出色易用性的同时,达到了业界领先的性能水平。
首先,我们将分析 vLLM 之前的性能瓶颈。然后,我们将介绍过去一个月内实施并落地的解决方案。最后,我们将展示最新 vLLM v0.6.0 版本与其他推理引擎的基准测试结果。
性能诊断
LLM 推理需要 CPU 和 GPU 之间的紧密协作。虽然主要计算发生在 GPU 上,但 CPU 在处理请求和服务调度方面也发挥着重要作用。如果 CPU 调度速度不够快,GPU 就会闲置等待,这最终导致 GPU 利用率低下并阻碍推理性能。
一年前,当 vLLM 首次发布时,我们主要针对内存有限的 GPU 上的相对较大模型进行了优化(例如 NVIDIA A100-40G 上的 Llama 13B)。随着具有更大内存的更快 GPU(如 NVIDIA H100)越来越普及,以及模型针对推理进行了更多优化(例如使用 GQA 和量化技术),推理引擎中 CPU 部分所消耗的时间成为了一个显著的瓶颈。具体而言,我们的分析结果显示,在 1 张 H100 GPU 上运行 Llama 3 8B 时:
- HTTP API 服务器占据了总执行时间的 33%。
- 29% 的总执行时间花在调度上,包括从上一步收集 LLM 结果、调度下一步要运行的请求,以及将这些请求准备为 LLM 的输入。
- 最后,只有 38% 的时间用于 LLM 的实际 GPU 执行。
通过上述基准测试,我们在 vLLM 中发现了两个主要问题:
- CPU 开销大。 vLLM 的 CPU 组件耗时惊人。为了使 vLLM 的代码易于理解和贡献,我们大部分代码使用 Python 编写,并使用了许多 Python 原生数据结构(如 List 和 Dict)。这成为了一个巨大的开销,导致调度和数据准备时间过长。
- 组件间缺乏异步性。 在 vLLM 中,许多组件(如调度器和输出处理器)以同步方式执行,这会阻塞 GPU 执行。这主要是因为:1) 我们最初假设模型执行会比 CPU 部分慢得多;2) 为了简化许多复杂调度情况(如束搜索调度)的实现。然而,这个问题导致 GPU 等待 CPU,降低了利用率。
总之,vLLM 的性能瓶颈主要是由阻塞 GPU 执行的 CPU 开销引起的。在 vLLM v0.6.0 中,我们引入了一系列优化来最小化这些开销。
性能增强
为了确保 GPU 能保持忙碌,我们进行了多项改进:
将 API 服务器和推理引擎分离到不同的进程中 (PR #6883)

通过仔细分析,我们发现管理网络请求和为 OpenAI 协议格式化响应会消耗相当多的 CPU 周期,尤其是在启用 token 流式传输的高负载下。例如,Llama3 8B 在轻负载下每 13 毫秒可以生成 1 个 token。这意味着前端每秒需要流式传输 76 个对象,且随着数百个并发请求,此需求进一步增加。这对之前版本的 vLLM 构成了挑战,因为 API 服务器和推理引擎在同一进程中运行。结果,推理引擎和 API 服务器协程不得不争抢 Python GIL,导致 CPU 竞争。
我们的解决方案是将处理请求验证、token 化和 JSON 格式化的 API 服务器与管理请求调度和模型推理的引擎分离开来。我们使用低开销的 ZMQ 连接这两个 Python 进程。通过消除 GIL 约束,两个组件都可以在没有 CPU 竞争的情况下更高效地运行,从而提高了性能。
即使在分离了这两个进程之后,我们发现引擎处理请求的方式以及与 HTTP 请求交互的方式仍有很大改进空间。我们正积极致力于进一步提高 API 服务器的性能 (PR #8157),旨在使之在不久的将来达到离线批处理推理的效率。
批量调度多个步骤 (PR #7000)

vLLM 中多步调度方法的示意图。通过一次性批量处理多个调度步骤,我们使 GPU 比以前更忙碌,从而降低了延迟并提高了吞吐量。
我们发现 vLLM 调度器和输入准备带来的 CPU 开销导致了 GPU 利用率不足,从而导致吞吐量不理想。为了解决这个问题,我们引入了多步调度(multi-step scheduling),它执行一次调度和输入准备,然后运行模型 n 个连续步骤。通过确保 GPU 可以在 n 个步骤之间持续处理而无需等待 CPU,这种方法将 CPU 开销分散到多个步骤中,显著减少了 GPU 空闲时间并提高了整体性能。
这使得在 4xH100 上运行 Llama 70B 模型的吞吐量提高了 28%。
异步输出处理 (PR #7049, #7921, #8050)

vLLM 中异步输出处理的示意图。通过将输出数据结构处理的 CPU 工作与 GPU 计算重叠,我们减少了 GPU 空闲时间并提高了吞吐量。
为了最大限度地提高 GPU 利用率,我们还彻底改进了 vLLM 处理模型输出的方式。
以前,在生成每个 token 后,vLLM 将模型输出从 GPU 移动到 CPU,检查停止条件以确定请求是否已完成,然后执行下一步。这种输出处理通常很慢,涉及对生成的 token ID 进行反 token 化和执行字符串匹配,且开销随批处理大小增加而增加。
为了解决这一低效问题,我们引入了异步输出处理,它将输出处理与模型执行重叠。vLLM 不再立即处理输出,而是推迟处理,在执行第 n+1 步的同时处理第 n 步的输出。这种方法假设第 n 步没有请求满足停止条件,这会产生每个请求多执行一步的轻微开销。然而,GPU 利用率的显著提升足以抵消这一成本,从而提高了整体性能。
这使得在 4xH100 上运行 Llama 70B 模型的每个输出 token 时间缩短了 8.7%。
其他优化
为了进一步减少 CPU 开销,我们仔细检查了整个代码库并执行了以下优化:
- 随着请求的到来和完成,Python 会反复分配和释放新对象。为了缓解这种开销,我们创建了一个对象缓存 (#7162) 来保存这些对象,这使端到端吞吐量显著提高了 24%。
- 在将数据从 CPU 发送到 GPU 时,我们尽可能使用非阻塞操作 (#7172)。CPU 可以在 GPU 复制数据时发起许多复制操作。
- vLLM 支持多种注意力后端和采样算法。对于具有简单采样请求的常用工作负载 (#7117),我们引入了一条跳过复杂步骤的快速代码路径。
在过去一个月里,vLLM 社区为这些优化付出了巨大努力。我们将继续优化代码库以提高效率。
性能基准测试
通过上述努力,我们很高兴地分享 vLLM 的性能与上个月相比有了很大提升。根据我们的性能基准测试,它达到了最先进的水平。
服务引擎。 我们将 vLLM v0.6.0 与 TensorRT-LLM r24.07、SGLang v0.3.0 和 lmdeploy v0.6.0a0 进行了基准测试。对于其他基准,我们使用其默认设置。对于 vLLM,我们通过设置 --num-scheduler-steps 10 开启了多步调度。我们正在积极努力将其设为默认开启。
数据集。 我们使用以下三个数据集对不同的服务引擎进行基准测试:
- ShareGPT:从 ShareGPT 数据集中随机采样 500 条提示词,并使用固定的随机种子。
- 平均输入 token:202,平均输出 token:179
- Prefill-heavy 数据集:从 sonnet 数据集中合成生成的 500 条提示词,平均约 462 个输入 token 和 16 个输出 token。
- Decode-heavy 数据集:从 sonnet 数据集中合成生成的 500 条提示词,平均约 462 个输入 token 和 256 个输出 token。
模型。 我们在两个模型上进行基准测试:Llama 3 8B 和 70B。我们没有使用最新的 Llama 3.1 模型,因为 TensorRT-LLM r24.07(使用 TensorRT LLM 后端 v0.11)不支持它 (问题链接)。
硬件。 我们使用 A100 和 H100 进行基准测试。它们是用于推理的两个主要高端 GPU。
指标。 我们评估以下指标:
- 首 token 时间 (TTFT,以毫秒为单位)。我们在图中显示了平均值和平均值的标准误差。
- 每个输出 token 的时间 (TPOT,以毫秒为单位)。我们在图中显示了平均值和平均值的标准误差。
- 吞吐量 (以每秒请求数为单位)。
- 吞吐量是在 QPS inf 下测量的(意味着所有请求同时到达)。
基准测试结果
在 ShareGPT 和 Decode-heavy 数据集中,vLLM 在服务 Llama-3 模型时在 H100 上实现了最高吞吐量。

在不同的工作负载中,与 H100 上的其他框架相比,vLLM 在 Llama 8B 和 70B 上实现了高吞吐量。
有关其余性能基准测试,以及首 token 时间 (TTFT) 和每个输出 token 时间 (TPOT) 的详细指标,请参阅附录以获取更多数据和分析。您可以关注此 GitHub issue以重现我们的基准测试。
当前优化的局限性。 虽然我们当前的优化带来了显著的吞吐量提升,但这些优化也存在性能权衡,特别是多步调度:
- Token 间延迟波动: 在我们当前的多步调度实现中,我们也会批量返回多个步骤的输出 token。从最终用户的角度来看,他们会收到一批批的回复 token。我们正在通过将中间 token 流式传输回引擎来解决这个问题。
- 低请求率下较高的 TTFT: 新请求只能在当前的多步执行完成后开始执行。因此,更高的
--num-scheduler-steps会导致低请求率下较高的 TTFT。我们的实验重点是高 QPS 下的排队延迟,因此这种影响在附录的结果中并不显著。
结论与未来工作
在本文中,我们讨论了 vLLM 中的性能增强,这些增强使吞吐量提高了 1.8-2.7 倍,并与其他推理引擎相当。我们致力于稳步提高性能,同时不断扩大我们的模型覆盖范围、硬件支持和多样化功能。对于本文讨论的功能,我们将继续强化它们以实现生产就绪。
重要的是,我们还将专注于改进 vLLM 的核心以降低复杂性,从而降低贡献门槛并释放更多的性能提升。
参与其中
如果您还没有更新,我们强烈建议您更新 vLLM 版本(参见说明此处)并亲自尝试!我们总是很高兴了解您的用例以及我们可以如何让 vLLM 变得更好。可以通过 vllm-questions@lists.berkeley.edu 联系 vLLM 团队。vLLM 也是一个社区项目,如果您有兴趣参与和贡献,欢迎查看我们的路线图,看看有哪些“入门友好”的问题(good first issues)可以解决。通过在 X 上关注我们获取更多更新。
如果您在湾区,可以在以下活动中见到 vLLM 团队:vLLM 与 NVIDIA 的第六次聚会 (09/09)、PyTorch 会议 (09/19)、CUDA MODE IRL 聚会 (09/21),以及Ray Summit 上首次设立的 vLLM 专场 (10/01-02)。
无论您身在何处,都别忘了注册参加在线的双周 vLLM 办公时间交流会!每两周都会讨论新的主题。下一次会议将深入探讨性能增强。
致谢
本博客文章由伯克利 vLLM 团队起草。性能提升来自于 vLLM 社区的共同努力:来自 Neural Magic 的 Robert Shaw,以及来自 IBM 的 Nick Hill 和 Joe Runde 主导了 API 服务器的重构;来自 UCSD 的 Will Lin,以及来自 Anyscale 的 Antoni Baum 和 Cody Yu 主导了多步调度工作;来自 Databricks 的 Megha Agarwal 和来自 Neural Magic 的 Alexander Matveev 主导了异步输出处理,还有许多 vLLM 社区贡献者贡献了各种优化。所有这些努力共同带来了巨大的性能提升。
附录
我们在本节中包含了详细的实验结果。
1xA100 上的 Llama 3 8B
在 Llama 3 8B 上,vLLM 在 ShareGPT 和 Decode-heavy 数据集上实现了与 TensorRT-LLM 和 SGLang 相当的 TTFT 和 TPOT。与其他引擎相比,LMDeploy 的 TPOT 更低,但 TTFT 通常更高。在吞吐量方面,TensorRT-LLM 在所有引擎中吞吐量最高,vLLM 在 ShareGPT 和 Decode-heavy 数据集上吞吐量位居第二。

4xA100 上的 Llama 3 70B
在 Llama 3 70B 上,vLLM、SGLang 和 TensorRT-LLM 具有相似的 TTFT 和 TPOT(LMDeploy 的 TPOT 更低,但 TTFT 更高)。在吞吐量方面,vLLM 在 ShareGPT 数据集上实现了最高吞吐量,在其他数据集上与其它引擎表现相当。

1xH100 上的 Llama 3 8B
vLLM 在 ShareGPT 和 Decode-heavy 数据集上实现了最先进的吞吐量,尽管在 Prefill-heavy 数据集上的吞吐量较低。

4xH100 上的 Llama 3 70B
vLLM 在 ShareGPT 和 Decode-heavy 数据集上具有最高吞吐量(尽管仅比 TensorRT-LLM 稍高),但 vLLM 在 Prefill-heavy 数据集上的吞吐量较低。
