vLLM 高吞吐推理系统全景拆解

2026年4月1日
"从引擎内核到分布式服务的系统拆解"
Shiyuh
Shiyuh
技术传道者/AI 应用落地

很多人用 vLLM,停留在两层认知:

但如果你继续往下问一句:它到底为什么快,内部到底是怎么组织起来的? 答案就不再是一个命令行参数,而是一整套围绕 调度KV Cache连续批处理多进程执行分布式服务 搭起来的系统工程。

这篇文章是对 vLLM 官方长文 Inside vLLM: Anatomy of a High-Throughput LLM Inference System 的中文本地化整理。

我不做逐段直译,而是按中文技术读者更容易吸收的方式,重组为一条从单机引擎到多机服务的理解路径。

原文作者是 Aleksa Gordic,分析基于 commit 42172ad,聚焦 vLLM 的 V1 engine。如果你希望建立对现代 LLM 推理系统的整体心智模型,这篇非常值得读。


1. 先给结论:vLLM 本质上解决的是什么问题

vLLM 要解决的不是“让模型跑起来”,而是:

你可以把它理解为一个分层系统:

  1. 最内层是 LLM Engine / Engine Core
  2. 中间层是 Scheduler + KV Cache Manager + Model Executor
  3. 外层是 Async Client + Load Balancer + API Server
  4. 再往外才是你熟悉的 OpenAI 兼容接口和服务部署命令

也就是说,vLLM 的重点从来不只是“模型推理”,而是高吞吐、低浪费、可扩展的推理系统设计


2. 从最小例子开始:一个离线引擎到底包含什么

原文先从一个最简单的离线推理例子讲起:

from vllm import LLM, SamplingParams
prompts = [
"Hello, my name is",
"The president of the United States is",
]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
llm = LLM(model="TinyLlama/TinyLlama-1.1B-Chat-v1.0")
outputs = llm.generate(prompts, sampling_params)

这个例子看起来很简单,但背后已经有一整套引擎结构在工作。 作者给出的核心拆分是:

其中最关键的是 Engine Core。它内部至少有三块:

如果你只记一句话,可以记这个:

vLLM 的性能,很大程度不是来自“模型本身”,而是来自对请求调度和 KV Cache 的管理方式。


3. 引擎启动时,真正重的事情发生在哪里

很多人以为 LLM(...) 只是“加载模型”。 其实在 vLLM 里,初始化阶段已经做了大量系统级准备工作。

3.1 Init device

这一步会做:

简单说,它不是只把模型丢到 GPU,而是在为后续高吞吐执行搭好缓冲区、索引结构和运行环境。

3.2 Load model

这部分比较直观:

3.3 Initialize KV cache

这一段才是重点中的重点。它会:

也就是说,vLLM 启动时并不是只在“准备推理”,而是在预构建一个可复用的高效执行环境。 这也是为什么它后面能把内核启动开销、KV Cache 命中率和批处理效率做得比较激进。


4. 请求进入引擎后,vLLM 是怎么跑起来的

generate() 的主线非常清晰,可以概括为两步:

  1. 把请求喂进引擎
  2. 循环执行 step()

每个请求进入系统时,会经历:

而后续真正反复执行的是 step()。 每次 step 都包含三件事:

  1. Schedule:这一步要跑哪些请求
  2. Forward pass:真正过模型
  3. Postprocess:写回 token、判停、释放资源

这三个阶段看起来普通,但基本就是整个 vLLM Engine 的最小运行循环。


5. vLLM 为什么能快:关键在调度器,而不是“批量大”

原文里一个非常重要的点是: vLLM 的核心优势,不是简单把请求拼成一个 batch,而是区分不同阶段的请求,并动态混合调度

5.1 两类完全不同的工作负载

推理系统里主要有两类请求阶段:

它们的性能特征非常不同:

为什么? 因为 decode 每次虽然只算一个 token,但仍然要把大模型权重搬出来参与计算。

这意味着,调度器不能把两者当成同一种东西处理。 vLLM V1 的一个重要改进,就是能在同一个 step 中更灵活地混合 prefill 和 decode,而不是像旧版本那样非此即彼。

5.2 调度器在干什么

调度器会优先看 running 队列里的 decode 请求,然后再处理 waiting 里的 prefill 请求。 每次调度都要考虑:

这里就引出一个核心方法:allocate_slots

它大致做三件事:

  1. 算出需要多少 KV blocks
  2. 检查 block pool 是否还有足够空间
  3. 真的把 block 分给请求

所以你可以把调度器理解成一个“请求编排器”,而 KV Cache Manager 更像“显存页分配器”。


6. PagedAttention 到底解决了什么

vLLM 最广为人知的关键词之一就是 PagedAttention。 但如果只停留在“这是一个很厉害的注意力优化”,其实并没有真正理解它。

更容易理解的方式是:

PagedAttention 的核心不是改写注意力公式,而是把 KV Cache 的内存管理做成类似分页系统。

也就是说,token 对应的 KV 不再要求必须连续地、大块地分配在显存里,而是被映射到一个个 block 上。 这样做的好处是:

KV Cache Manager 会维护一个 free_block_queue,里面是所有可用的 KV blocks。 请求需要缓存时,从里面取;请求结束时,再把 blocks 还回去。

这就是为什么作者会说: KV Cache Manager 是 PagedAttention 的心脏。


7. 连续批处理为什么重要

如果你只做离线推理,一次性把 prompts 全部喂进去,问题还相对简单。 但在线服务不是这样:

这时如果你还坚持“一个固定 batch 从头跑到尾”,资源利用会很差。

vLLM 的做法是 continuous batching,也就是连续批处理:

这就是为什么它不需要传统意义上的右侧 padding 批处理,而是把不同请求压到一个统一执行流中。

这套机制跟 PagedAttention 配合起来,才真正构成了 vLLM 的高吞吐基础。


8. 五个必须理解的高级特性

原文后半部分进入高级能力。这里我建议你重点抓五个概念。

8.1 Chunked Prefill

问题背景很简单: 如果有一个超长 prompt,一次 prefill 可能会占满整个 step,导致其他请求全部排队。

Chunked prefill 的思路就是把长 prompt 切块处理。 你可以把它理解成:

对于真实线上服务,这一点很重要,因为长 prompt 一旦不加控制,很容易拖垮整体交互体验。

8.2 Prefix Caching

这是另一个非常实用的特性。 如果多个请求共享相同前缀,就没必要反复重新计算同样的 token。

vLLM 的做法是:

本质上就是:

不要重复做已经做过的 prefill。

这对共享 system prompt、长上下文模板、多轮 agent 工作流都非常有价值。

8.3 Guided Decoding

如果你需要 JSON、语法约束、结构化输出,就会遇到 guided decoding。

这里的核心思路是:

这样模型最终只能生成符合目标结构的结果。

作者这里提到,vLLM 会通过类似 xgrammar 的后端来完成语法编译与 mask 维护。 所以 guided decoding 本质上不是“采样技巧”,而是把形式语言约束融入解码循环

8.4 Speculative Decoding

这是近两年推理加速里很重要的一条线。 它的基本思想是:

原理上,它是在保持最终分布一致的前提下,尽量减少“大模型一 token 一 forward”的低效过程。

vLLM V1 里重点支持的提案方式包括:

这块如果你做高并发推理优化,非常值得重点看。

8.5 Disaggregated P/D

这里的 P/D 指的是 Prefill / Decode 分离。

这是一个很有系统味道的设计:

既然两者资源特征不同,那就没必要强绑在同一执行实例里。 于是就有了:

这样做的好处是,你可以分别优化 TTFTITL,而不是把一切问题都扔给一个统一实例硬扛。


9. 从单 GPU 到多 GPU:为什么需要 MultiProcExecutor

当模型太大,单卡装不下时,问题就进入并行执行阶段。

此时 vLLM 的核心执行器会从 UniProcExecutor 过渡到 MultiProcExecutor

它负责的事情可以概括为:

从调用者角度看,接口几乎没变,依然是 execute_model。 但在内部,已经从“单进程直接调 worker”变成了“父进程协调多个 GPU worker 并做跨进程通信”。

这也是一个很典型的系统工程思想:

对上层接口尽量保持稳定,把复杂性吸收在执行层内部。


10. 从单机推理到在线服务:vLLM 的外层服务栈怎么搭

很多人看到 vllm serve 就以为事情结束了。 但原文真正精彩的地方,是把在线服务的内部路径讲得非常具体。

10.1 Headless 节点在做什么

在多节点场景下,headless server 节点负责:

也就是说,它更像“纯算力后端”。

10.2 API 节点在做什么

API server 节点上会跑:

这里的关键不只是“收请求”,而是:

也就是说,在线服务层本质上是一层:

异步前端 + 负载均衡 + 分布式执行协调器

10.3 一个请求的完整生命周期

原文最后把一次 /v1/completions 请求的路径完整串起来了。 压缩成中文,就是这条链:

  1. 请求打到 FastAPI 路由
  2. 路由层做 tokenize 和 metadata 封装
  3. AsyncLLM.generate() 把请求交给异步客户端
  4. 负载均衡逻辑选一个合适的 engine
  5. 请求进入对应 engine 的 input socket
  6. engine 主线程反复调用 step()
  7. 输出线程把结果发回前端
  8. API server 再把响应回给调用方

这样你会发现: 用户看到的是一个 HTTP 接口,但系统内部其实是异步任务、消息队列、进程编排、GPU worker 和缓存管理共同完成的一条流水线。


11. 性能指标到底该怎么看:不是只盯 tokens/s

如果你只用一个指标评价推理系统,通常会犯错。

原文把几个核心指标讲得很清楚:

为什么一定要区分这些指标?

因为在线交互和离线批处理的优化目标完全不同:

而这两者往往互相拉扯。

11.1 延迟和吞吐为什么会冲突

作者用 roofline 视角解释了一个常见现象:

尤其 decode 阶段经常是带宽受限,而不是纯算力受限。 所以“多塞点请求进去”不一定总是更优,关键在于你想优化哪类指标。

这也是为什么 vLLM 里会有:

这些看似分散的特性,本质上都在服务同一个目标: 更精细地调和延迟与吞吐。


12. vLLM 官方是怎么做 benchmark 的

原文还提到 vLLM 自带的 benchmark 能力:

Terminal window
vllm bench latency
--model <model-name>
--input-tokens 32
--output-tokens 128
--batch-size 8

此外还有:

三者分别偏向:

这也提醒我们一件事:

没有脱离场景的“推理性能”。

你必须先明确自己是在做:

然后再看应该重点关心哪一组指标。


13. 这篇文章最值得带走的 6 个认知

读完这篇 vLLM 长文,我觉得最值得保留的不是具体类名,而是下面这 6 个系统认知:

1) LLM 推理优化首先是系统问题,不只是算子问题

真正影响线上表现的,往往不是一个 kernel,而是:

2) Prefill 和 Decode 是两种不同世界

它们计算特征不同,指标诉求不同,最优调度方式也不同。 理解这一点,才容易理解为什么会有 chunked prefill、spec decode、P/D 分离。

3) KV Cache 是推理系统的基础设施

PagedAttention、prefix caching、外部 KV transfer,本质上都围绕 KV Cache 展开。 谁把 KV Cache 管得好,谁就更容易做出高吞吐系统。

4) “持续批处理”比“静态大 batch”更贴近真实线上环境

线上请求是流式进入的,不会乖乖排队等你凑齐一个完美 batch。 所以 continuous batching 才是更现实的优化方向。

5) 高吞吐的代价是架构复杂度

从 UniProc 到 MultiProc,再到 DP 协调和 API server,复杂度是层层叠加的。 vLLM 的价值之一,就是把这份复杂度尽可能封装掉。

6) 性能优化永远要和业务指标绑在一起

如果你的目标是聊天体验,就别只盯总 tokens/s。 如果你的目标是离线吞吐,也别为了首 token 漂亮数字牺牲整体产能。


14. 谁最应该读原文

我觉得这篇最适合三类人:

如果你只是想“把服务跑起来”,官方文档可能已经够用。 但如果你想真正理解现代 LLM serving 是如何从单机脚本演化成分布式推理系统的,这篇原文非常值得精读。


15. 延伸阅读

如果你后续还想继续往深处读,原文中尤其建议重点延伸这几个主题:


16. 最后的话

这篇文章最打动我的,不是它列了多少特性,而是它把一个现代推理系统拆得非常诚实:

这样的写法非常适合建立全局视角。

如果你最近正好在看推理框架,或者在做自己的 LLM serving 栈,我会建议你把这篇当成一篇“系统地图”来读。 哪怕你暂时不需要记住所有类名,只要抓住它背后的设计逻辑,也会对后续很多工程判断很有帮助。

准备好开始您的 AI 之旅了吗?

读完这篇文章,想必您对 AI 技术有了更深的了解。现在就来体验共绩算力,让您的想法快速变成现实。

✓ 已有 10 万 + 开发者在使用

✓ 99.9% 服务可用性

✓ 开箱即用的容器托管