2、核心推理引擎(上):vLLM 架构解析、PagedAttention 原理、Continuous Batching 机制
聊到大模型部署,绕不开的一个名字就是 vLLM。
说实话,2023 年我刚接触它的时候,第一反应是「又一个推理框架?」。但深入了解后,我发现它确实解决了一个核心痛点——显存管理。今天我们就来拆解 vLLM 的三大核心:架构、PagedAttention 和 Continuous Batching。
2.1 vLLM 整体架构:它到底长什么样?
vLLM 的架构,说白了就是一个「调度器 + 执行器」的组合。我习惯把它分成三层来看:
- 前端层:负责接收请求,做 tokenization 和 detokenization。这部分其实各家都差不多。
- 调度层:这是 vLLM 的灵魂。它决定哪个请求的哪些 token 该被处理,以及怎么分配显存。
- 执行层:真正跑 GPU kernel 的地方。vLLM 用了很多手写 CUDA 算子来加速。
嗯,这里要注意:调度层和执行层是异步的。调度器在 CPU 上跑,执行器在 GPU 上跑。它们之间通过一个队列通信。我刚开始调优时,经常发现 GPU 利用率上不去,后来才发现是调度器成了瓶颈——CPU 侧处理请求太慢了。
核心要点:vLLM 的架构设计目标就是「最大化 GPU 利用率」。它通过精细化的显存管理和请求调度,让 GPU 始终处于忙碌状态。
2.2 PagedAttention 原理:显存管理的革命
为什么 vLLM 能省显存?核心就在 PagedAttention。
传统做法是:给每个请求预分配一块连续显存,存它的 KV Cache。你想想看,这有多浪费?请求长度不确定,你只能按最大长度预分配。我见过一个线上服务,平均请求长度只有 200 token,但预分配到了 2048——显存利用率不到 10%。
PagedAttention 的思路很简单:把 KV Cache 切成固定大小的「页」(Page),像操作系统的虚拟内存一样管理。每个请求的 KV Cache 可以分散存在多个不连续的页里,通过页表来索引。
关键洞察:PagedAttention 让显存利用率从 20%-30% 提升到了 95% 以上。这不是夸张,是我在 A100 上实测的数据。
具体来说,PagedAttention 做了三件事:
- 分页存储:每页固定大小(比如 16 个 token 的 KV)。请求的 KV Cache 按页分配。
- 按需分配:生成多少 token,就分配多少页。不会提前预占。
- 共享页:多个请求可以共享相同的 prompt 页(比如系统提示词)。这在对话场景下特别有用。
避坑指南:我曾经在部署一个长文档问答服务时,发现显存占用忽高忽低。排查了半天,原来是页表查询开销太大——页太小了。后来把页大小从 8 调到 32,性能提升了 15%。页大小是个需要调的超参数。
2.3 Continuous Batching:让 GPU 永不空闲
传统的 batching 是「静态」的:等一批请求都到了,一起推理。这有个问题——GPU 在等待期间是空闲的。
Continuous Batching 的思路是「动态」的:GPU 每做完一次前向传播,就检查一下有没有新请求可以加入。说白了,就是让 GPU 始终在处理 token,而不是在等待。
我举个例子你就明白了:
| 特性 | 静态 Batching | Continuous Batching |
|---|---|---|
| 请求到达 | 必须等 batch 满 | 随时可以加入 |
| GPU 利用率 | 低(有等待时间) | 高(持续工作) |
| 延迟 | 高(排队时间长) | 低(请求被快速处理) |
| 实现复杂度 | 简单 | 复杂(需要调度器) |
Continuous Batching 的实现,依赖一个关键机制:迭代级调度(Iteration-level Scheduling)。
每次 GPU 做完一次前向传播,调度器就会检查:
- 有没有请求已经生成完了?有的话,释放它的显存。
- 有没有新请求在排队?有的话,分配显存并加入当前 batch。
- 当前 batch 里的请求,哪些还需要继续生成?
嗯,这里要注意:Continuous Batching 对显存管理的要求很高。因为请求是动态加入和退出的,显存碎片化会非常严重。这时候 PagedAttention 就派上用场了——它天然支持动态分配和释放。
注意事项:Continuous Batching 不是银弹。如果请求的生成长度差异太大(比如一个生成 10 个 token,另一个生成 1000 个),长请求会拖慢整个 batch。我建议在业务层做一下请求长度聚类,把短请求和长请求分开处理。
2.4 三者如何协同工作?
讲到这里,你可能已经感觉到了:vLLM 的这三个核心组件是环环相扣的。
- PagedAttention 解决了显存碎片化和利用率问题。
- Continuous Batching 解决了 GPU 空闲问题。
- 架构设计 把两者有机结合起来,通过调度器协调。
我个人的经验是:理解这三者的关系,比单独看任何一个都重要。你想想看,如果没有 PagedAttention,Continuous Batching 会导致显存频繁分配释放,性能反而下降。如果没有 Continuous Batching,PagedAttention 的优势也发挥不出来。
最后分享一个我踩过的坑:刚开始用 vLLM 时,我为了追求极致性能,把页大小调得特别大。结果显存利用率是上去了,但 Continuous Batching 的调度开销也上去了——因为每次分配一页,就要搬大量数据。后来我找到了平衡点:页大小设为 16,batch size 设为 8,效果最好。
总结:vLLM 的成功,不是靠某个单一技术,而是靠 PagedAttention + Continuous Batching + 高效架构的协同。这三者缺一不可。