vLLM 推理内核深度解析
前言:走进 LLM 推理引擎的心脏
前言:走进 LLM 推理引擎的心脏
"The best engineering is indistinguishable from physics — it makes the impossible feel inevitable."
推理引擎是 AI 应用真正的瓶颈。模型再强,喂不进 GPU 也是白搭。
一切的起点:伯克利实验室那条 40% 的利用率曲线
2023 年春,加州大学伯克利分校一间挤满了监控屏的实验室里,几个研究生盯着 GPU 利用率曲线发愁。
他们在用 HuggingFace Transformers 部署一个 13B 参数的语言模型,GPU 显存看上去已经被占满,但 GPU 计算核的利用率始终在 40% 上下徘徊——大量显存被浪费在碎片化的 KV Cache 分配上,请求排着队,首 Token 延迟飙升,而 GPU 的 TensorCore 却一直在"发呆"。
GPU 利用率: ████████████░░░░░░░░░░░░░░░░░░ 40%
GPU 显存占用: ██████████████████████████████ 95%
实际有效显存: ████████████████░░░░░░░░░░░░░░ 40%
浪费在碎片上: ░░░░░░░░░░░░░░░░██████████████ 55%
"操作系统早就解决了这个问题,"其中一个人说,"内存分页。"
这句话成了一切的起点。
从 OS 到 GPU:一次跨领域的思想迁移
他们把操作系统里虚拟内存的分页机制引入到 KV Cache 管理中,设计出 PagedAttention 算法。KV Cache 不再按请求整块预留,而是被切分为固定大小的 Block,按需映射,动态回收——就像操作系统管理物理内存一样优雅。
传统 KV Cache:
请求 A: ████████████████──────── (预留 512,实际用 320,浪费 192)
请求 B: ████████──────────────── (预留 512,实际用 160,浪费 352)
PagedAttention:
Block Pool: [B1][B2][B3][B4][B5][B6][B7][B8][B9][B10]...
请求 A → 用 B1/B3/B7 (按需分配)
请求 B → 用 B2/B5 (按需分配)
释放的 B4/B6 → 立即给新请求 (动态回收)
这个想法最终凝结成一篇 SOSP'23 论文和一个开源项目——vLLM。
论文发表后不到一年,vLLM 的 GitHub 星标突破 6 万。从硅谷创业公司到国内大厂,从学术实验室到千卡集群,几乎所有严肃对待 LLM 推理效率的团队都在用它。它成了 LLM 推理基础设施的事实标准。
一个数字说明一切
2023 年 H100 单卡价格: $30,000
全球部署的 H100 数量: ~300 万张 (2025 年估计)
总硬件价值: ~$900 亿
如果推理效率提升 2 倍:
等价节省: $450 亿的硬件成本
或者: 服务 2 倍的用户
每提升一个百分点的推理效率,都是在给 AI 产业腾挪万亿级的空间。vLLM 的历史地位就建立在这个数字上。
为什么又一本 vLLM 书
如果你搜 "vLLM tutorial",能找到几十篇博客、十几个视频教程、一份越来越厚的官方文档。它们大多数的套路是:
- 怎么安装 vLLM
- 怎么启动一个 OpenAI 兼容服务
- 有哪些命令行参数
- 遇到 OOM 怎么办
这些都有用,但合起来仍然只是**"使用手册"**——告诉你按哪些按钮,却不告诉你按钮背后有什么线路。
工程师真正想知道的
而工程师真正想知道的是另一组问题:
- 为什么 V1 引擎要从单进程拆成多进程?多出来的 ZMQ 通信成本从哪里赚回来?
- 连续批处理里那个神秘的"预算"究竟怎么算?
max_num_batched_tokens调大调小各自有什么代价? - PagedAttention 到底是怎么让一个不连续的 KV Cache 跑 attention 的?Block Table 的间接寻址不会拖慢 kernel 吗?
- 前缀缓存为什么能做到"命中一块就省一块预算"?它怎么和抢占机制互动?
- 调度器决定谁进 batch、谁被抢占时究竟怎么选受害者?
- Worker 为什么从 V0 的无状态变成 V1 的有状态?差量同步的可靠性边界在哪里?
- CUDA Graph、torch.compile、FlashAttention-3 三套优化是怎么叠加的?
- 当流量上来,系统的第一个瓶颈会出现在哪里,第二个、第三个又在哪里?
- Chunked Prefill 如何避免 prefill 独占 GPU 而让 decode 请求饿死?
- 投机解码是怎么在保证采样分布不变的前提下获得 2-3x 加速的?
这些"为什么"藏在代码注释里、PR 讨论里、issue 追踪里、作者在 GTC 大会上的一句口误里。你要把它们拼起来,才能真正理解 vLLM。
本书的定位
本书做的就是这件事——把源码、PR 讨论、benchmark 数据和生产踩坑经验串成可读的章节——不是源码导读,是源码之后的思考。
graph LR
A["官方文档<br/>"怎么用""] --> B["社区博客<br/>"为什么这样用""]
B --> C["源码<br/>"到底是怎么做的""]
C --> D["本书<br/>"为什么这样设计""]
style A fill:#fef3c7,stroke:#f59e0b
style B fill:#fef3c7,stroke:#f59e0b
style C fill:#dbeafe,stroke:#3b82f6
style D fill:#dcfce7,stroke:#22c55e,stroke-width:2px
本书基于的源码版本
本书基于 vLLM v0.8.x,这是一个里程碑版本:V1 引擎在这个版本中正式成为默认架构。
V1 是对整个推理引擎的重新设计——多进程架构、统一 Token 调度、零开销前缀缓存、分段 CUDA 图、有状态 Worker——几乎每个核心子系统都被重写过。
为什么选 V1 而非 V0
选择 v0.8.x 而不是更早的 V0 版本,是因为 V1 代表了 vLLM 团队对"推理引擎应该长什么样"这个问题的最新答案。理解 V1,就是理解 LLM 推理系统设计的当前边界。
源码对照指南
我建议你在阅读时同步打开源码对照:
git clone --branch v0.8.5 https://github.com/vllm-project/vllm.git
cd vllm
# V1 引擎核心路径
ls vllm/v1/engine/ # EngineCore、EngineCoreProc、EngineCoreClient
ls vllm/v1/core/ # Scheduler、KVCacheManager、BlockPool
ls vllm/v1/worker/ # Worker、GPUModelRunner、InputBatch
ls vllm/v1/executor/ # UniProc / Multiproc / Ray 三种执行器
ls vllm/v1/attention/ # FlashAttention / FlashInfer 等 Attention 后端
# 为对照理解 V1 重构动因,保留一份 V0 源码很有帮助
ls vllm/engine/ # V0 的 LLMEngine / AsyncLLMEngine
ls vllm/core/ # V0 的 Scheduler / BlockSpaceManager
引用标注规范
书中所有源码引用都标注了具体路径,形如:
vllm/v1/engine/core.py:142 ← 精确行号引用
vllm/v1/core/sched/scheduler.py ← 模块级引用
遇到一个精确的行号引用,说明那一行的代码足够关键,值得你停下来对照读一遍。
为什么是 vLLM
在 vLLM 之前,LLM 推理领域并不缺方案:
| 方案 | 擅长 | 不足 |
|---|---|---|
| HuggingFace Transformers | 直接从训练流程衔接,生态最广 | 推理效率低,批处理原生支持弱 |
| NVIDIA TensorRT-LLM | 极致 kernel 优化,延迟最低 | C++ 为主,模型转换门槛高,迭代慢 |
| DeepSpeed-Inference | 大模型 TP / PP 的工程先行者 | 后续维护放缓,社区活跃度下降 |
| llama.cpp | CPU / Apple Silicon 本地推理之王 | 不是为多卡云端服务设计的 |
| SGLang / LMDeploy / MLC-LLM | 新一代竞争者 | 功能覆盖不如 vLLM 全 |
| Ollama | 桌面端开箱即用 | 单用户 only,不适合服务化 |
vLLM 找到的黄金平衡点
vLLM 找到的是一个黄金平衡点:
- 比 Transformers 快 14-24 倍(来自原论文的对照实验,在真实混合负载下)
- 比 TensorRT-LLM 易用百倍——纯 Python 接口,
pip install vllm就跑,不需要模型转换、不需要 C++ 编译 - 架构开放且社区驱动——Apache 2.0 协议,超过 2000 位贡献者,PR 平均 48 小时内被 review
无与伦比的教育价值
更重要的是,vLLM 源码的教育价值独一无二:
| 项目 | 复杂度 | 学习成本 |
|---|---|---|
| TensorRT-LLM | CUDA + C++ 深水区 | 一个 kernel 看一天 |
| llama.cpp | GGML 后端 | 对 Python 工程师陌生 |
| HF Transformers | 训练 + 推理混合 | 路径被"污染"得复杂 |
| vLLM | Python + 必要 CUDA | 一两小时读懂一个子系统 |
vLLM 用 Python(加上必要的 CUDA kernel)实现了生产级的推理引擎,代码可读性和系统复杂度之比恰到好处——足够复杂能教会你真实的工程权衡,又足够清晰能让你在一两小时内读懂一个子系统。
对想"真正理解推理引擎"的工程师来说,vLLM 就是当下最好的教材。
V0 → V1:一次激进且必要的重构
V0 是 vLLM 的第一代实现,从 2023 到 2024 年支撑了社区爆发性增长。但代码积累到一定程度,V0 开始显露出难以修补的病:
V0 的五大病症
| 症状 | 表现 | 影响 |
|---|---|---|
| God Object | LLMEngine 单文件 1500+ 行 |
维护噩梦 |
| 调度重复 | Prefill/Decode/Chunked 三套代码 | Bug 容易,优化难做 |
| 无状态 Worker | 每 step 广播 KB 级冗余数据 | 网络成为瓶颈 |
| 职责耦合 | Block 分配/前缀缓存/抢占在一起 | 改一处动全身 |
| 单进程 | API 和 Engine 在同一个进程 | 前端压力卡住推理 |
V1 的七大架构革新
2024 年下半年 vLLM 团队启动 V1 重构。V1 不是"给 V0 打补丁",是推倒重来的架构级改造。主要思想:
graph TD
V1[V1 架构革新]
V1 --> F1[① 单线程 EngineCore 主循环<br/>消除并发复杂度]
V1 --> F2[② 统一 Token 调度<br/>Prefill/Decode/Spec 都是分配 N 个 Token]
V1 --> F3[③ Block Pool / KVCache / Scheduler 解耦<br/>每层只做自己的事]
V1 --> F4[④ 有状态 Worker + 差量同步<br/>O-全状态 → O-变化量]
V1 --> F5[⑤ 多进程 + ZMQ<br/>API 和 Engine 分离]
V1 --> F6[⑥ Executor 抽象<br/>UniProc/Multiproc/Ray 同一接口]
V1 --> F7[⑦ Attention Backend 抽象<br/>Flash/FlashInfer/Triton/ROCm 可插拔]
style V1 fill:#dbeafe,stroke:#3b82f6,stroke-width:2px
- 单线程 EngineCore 主循环——消除并发复杂度
- 统一 Token 调度——Prefill / Decode / Spec Decode 都是"给请求分配 N 个 Token"
- Block Pool / KVCacheManager / Scheduler 解耦——每层只做自己的事
- 有状态 Worker + 差量同步——通信量从 O(总状态) 降到 O(变化量)
- 多进程 + ZMQ——API Server 和 EngineCore 分离,前端扩展不阻塞推理
- Executor 抽象层——UniProc / Multiproc / Ray 同一套接口,部署拓扑和业务逻辑彻底脱钩
- Attention Backend 抽象——FlashAttention-2/3、FlashInfer、Triton、ROCm 可插拔
V0 的重要组件在 V1 几乎都有对应的重新实现,但形态完全不同。本书以 V1 为主线讲解,V0 的历史经验作为对照出现——"当年的 V0 是怎么做的 / 后来为什么要改"是理解 V1 设计意图的最短路径。
本书的读者
本书面向这几类人:
| 读者 | 典型问题 | 对应章节 |
|---|---|---|
| AI 基础设施工程师 | 生产部署、容量规划、调优 | ch02/ch03/ch14/ch17 |
| 推理系统研发者 | 架构设计、工程实践 | ch01-ch18 全书 |
| 研究者 | 新算法的工程实现 | ch04/ch05/ch10/ch12 |
| 技术好奇者 | "数十亿 Token 怎么跑" | ch01 + ch18 |
| 模型部署工程师 | 选型、参数调优 | ch03/ch08/ch13/ch16 |
前置知识
你不需要精通 CUDA C++,但需要具备:
- 熟练 Python(读懂 async / multiprocessing / shared_memory 等标准库)
- Transformer 基础(QKV、Attention、Softmax、LayerNorm 这些词能说出个大概)
- 对深度学习推理有基本概念(知道什么是前向传播、什么是 logits)
- 基本的 GPU 知识(什么是显存、什么是 TensorCore)
- 操作系统基础(进程、虚拟内存的概念——PagedAttention 直接借用这些思想)
没有这些前置也能读,只是在某些段落需要查一下维基。
本书的组织
全书 18 章,按"从外到内 → 从核心到外围"的顺序组织:
graph TD
P1[第一篇 · 全景<br/>ch01]
P2[第二篇 · 引擎核心<br/>ch02-05]
P3[第三篇 · 执行层<br/>ch06-09]
P4[第四篇 · 性能优化<br/>ch10-13]
P5[第五篇 · 分布式与工程<br/>ch14-18]
P1 --> P2 --> P3 --> P4 --> P5
style P1 fill:#dbeafe,stroke:#3b82f6
style P2 fill:#fef3c7,stroke:#f59e0b
style P3 fill:#dcfce7,stroke:#22c55e
style P4 fill:#f3e8ff,stroke:#a855f7
style P5 fill:#fce7f3,stroke:#ec4899
第一篇 · 全景(第 1 章) : 从一个推理请求的完整生命旅程出发,建立对整个系统的直觉。这是全书的地图。
第二篇 · 引擎核心(第 2-5 章) : 深入 vLLM 的心脏: : - ch02 EngineCore —— 协调者的艺术 : - ch03 Scheduler —— Token 的交通指挥 : - ch04 PagedAttention —— 虚拟内存的启示 : - ch05 KV Cache Manager —— 寸土寸金的显存
第三篇 · 执行层(第 6-9 章) : 从调度决策到 GPU 计算: : - ch06 Worker & Executor —— GPU 军团 : - ch07 模型加载 —— 权重从磁盘到显存 : - ch08 ModelRunner —— 前向计算与 CUDA Graph : - ch09 采样 —— 从 logits 到 Token
第四篇 · 性能优化(第 10-13 章) : 让 vLLM 快人一步的四把利器: : - ch10 前缀缓存 —— 零开销的"记忆" : - ch11 Chunked Prefill —— 把长 prompt 切开 : - ch12 投机解码 —— 以小博大 : - ch13 量化 —— 精度与速度的取舍
第五篇 · 分布式与工程(第 14-18 章) : 从单机走向千卡: : - ch14 分布式并行 —— TP / PP / EP / DP : - ch15 多模态 —— 图像视频入场 : - ch16 LoRA 热切换 —— 一个引擎服务百个微调 : - ch17 API Server & 生产部署 —— 到一线士兵 : - ch18 设计哲学 —— 回看整幅画
每一章的写作节奏
每一章都遵循同样的节奏:
为什么需要 → 怎么设计 → 怎么实现 → 生产中怎么调
(问题域) (架构思想) (源码解析) (实战经验)
理论 → 设计 → 代码 → 实战,循环往复,螺旋上升。
阅读建议
线性读
从第 1 章读到第 18 章,建立完整的知识地图。适合第一次系统学 vLLM 的读者。
模块读
如果你已经熟悉 LLM 推理,直接进第 2-5 章引擎核心,再根据兴趣跳读后面的优化章节。适合想"快速看懂 vLLM 做了什么"的工程师。
问题驱动读
带着具体问题读对应章节:
| 生产问题 | 对应章节 |
|---|---|
| 线上 p99 TTFT 不稳定 | ch03 Scheduler + ch11 Chunked Prefill |
| KV OOM 频繁 | ch04 PagedAttention + ch05 KV Cache + ch10 Prefix Cache |
| 想加一个新模型 | ch07 模型加载 + ch08 ModelRunner |
| 想做低延迟推理 | ch12 Spec Decode + ch13 量化 + ch08 CUDA Graph |
| 想上多机部署 | ch06 Executor + ch14 分布式 |
| 想做多租户服务 | ch16 LoRA + ch17 API Server |
| 想加新的采样策略 | ch09 采样 |
| 想优化 batch 大小 | ch03 Scheduler + ch10 Prefix Cache |
| 想支持图片输入 | ch15 多模态 |
| 想做 8-bit/4-bit 部署 | ch13 量化 |
反复读
好的系统设计需要反复回味。第一遍看觉得复杂,第二遍看理解动机,第三遍看感叹优雅。
vLLM 的很多段代码——第一次读和第五次读的理解完全不是一个层次。耐心反复,是读懂它的必要代价。
动手实验:跟着书一起跑 benchmark
每一章都鼓励你亲手实验,不只是读。vLLM 团队提供了现成的 benchmark 工具:
# 启动一个服务
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Meta-Llama-3-8B-Instruct \
--max-model-len 8192
# 跑 benchmark
python benchmarks/benchmark_serving.py \
--model meta-llama/Meta-Llama-3-8B-Instruct \
--dataset-name sharegpt \
--num-prompts 200
# 对比调度参数
python benchmarks/benchmark_serving.py \
... --max-num-batched-tokens 2048 # 默认
python benchmarks/benchmark_serving.py \
... --max-num-batched-tokens 8192 # 更大
边读边跑,比任何文档都更深入。
一点私心
推理引擎是 AI 基础设施里最"隐形"的一层——用户看到的是 ChatGPT 流畅的回答,应用开发者看到的是 OpenAI 兼容的 API,但很少有人关心中间那个把 Token 一个一个"挤"出来的引擎在干什么。
它就像自来水管道——打开水龙头就有水,但没人想过水是怎么从水库送到你家的。
我希望这本书能让更多人看到管道里的风景。
为什么这很重要
不是每个人都需要自己造管道。但理解管道的人,才能在需要时修好它、改进它,甚至设计出更好的管道。
在 AI 大规模落地的今天,推理效率决定了 AI 能服务多少人:
- 每省下 10% GPU 成本 → 10% 以上的用户能被服务到
- 某个原本成本不合算的产品形态变得可能
- 某个需要 P99 TTFT < 200ms 的实时场景变得可行
理解推理引擎,就是理解 AI 普惠的底层逻辑。
源码不会骗人
vLLM 源码里很多设计第一遍读会疑惑"为什么要这么复杂",对照 issue 和 PR 才理解它解决的是哪个具体问题,再回头读就能看出它的优雅。本书尝试把这个"为什么"前置给读者,让你少走那两遍。
源码不会骗人。框架在变、API 在变,但好的设计思想不会过时。
vLLM 今天用 PagedAttention,明天可能有更好的算法替代它——但"用操作系统的思想管理 GPU 资源"这个跨领域思维,会在你未来的工程生涯里反复发挥价值。
让我们开始这段旅程。
杨艺韬 2026 年 4 月 · 于北京
源码导航
- GitHub: vllm-project/vllm
- 本书基准分支: v0.8.5
- PagedAttention 原论文: Kwon et al., "Efficient Memory Management for Large Language Model Serving with PagedAttention", SOSP 2023 (arXiv:2309.06180)
- FlashAttention 系列: Dao et al., NeurIPS 2022/2023
- V1 RFC: vLLM V1 Roadmap Discussion
- 杨艺韬讲堂 vLLM 系列首页: https://yangyitao.com/books/vllm/