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 始终处于忙碌状态。

前端层 请求接收 Tokenization 调度层 显存管理 请求调度 执行层 GPU Kernel 推理计算 vLLM 三层架构示意图 异步队列 调度器 → 执行器

2.2 PagedAttention 原理:显存管理的革命

为什么 vLLM 能省显存?核心就在 PagedAttention。

传统做法是:给每个请求预分配一块连续显存,存它的 KV Cache。你想想看,这有多浪费?请求长度不确定,你只能按最大长度预分配。我见过一个线上服务,平均请求长度只有 200 token,但预分配到了 2048——显存利用率不到 10%。

PagedAttention 的思路很简单:把 KV Cache 切成固定大小的「页」(Page),像操作系统的虚拟内存一样管理。每个请求的 KV Cache 可以分散存在多个不连续的页里,通过页表来索引。

关键洞察:PagedAttention 让显存利用率从 20%-30% 提升到了 95% 以上。这不是夸张,是我在 A100 上实测的数据。

具体来说,PagedAttention 做了三件事:

  1. 分页存储:每页固定大小(比如 16 个 token 的 KV)。请求的 KV Cache 按页分配。
  2. 按需分配:生成多少 token,就分配多少页。不会提前预占。
  3. 共享页:多个请求可以共享相同的 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 + 高效架构的协同。这三者缺一不可。


专注资料整理