1. vLLM 初探:什么是 vLLM?为什么需要它?PagedAttention 原理简介

1.1 大模型推理的痛点:我踩过的坑

先说说我自己的经历。几年前我刚接触大模型推理时,用的是最朴素的 Hugging Face Transformers 库。跑一个 7B 模型,单条请求还好,一旦并发上来,显存直接爆掉。更头疼的是——明明显存还有空间,但就是放不下新的请求。

为什么会这样?说白了,传统推理框架的内存管理太粗糙了。每个请求进来,系统会预先分配一大块连续显存,用来存 KV cache(键值缓存)。但问题是,你根本不知道这个请求会生成多长的输出。预分配少了,生成到一半显存不够;预分配多了,又白白浪费。

我印象很深,有一次线上服务,用户请求的生成长度差异特别大。有的只生成 10 个 token,有的要生成 2000 个。结果呢?显存碎片化严重,利用率不到 40%。那段时间我天天盯着监控面板,心里那个急啊。

核心痛点总结:
  • 显存浪费:预分配策略导致大量空间闲置
  • 碎片化:不同长度的请求互相干扰
  • 并发受限:显存利用率低,QPS 上不去

1.2 vLLM 是什么?

vLLM 就是来解决这些问题的。它是一个高性能的大模型推理引擎,由加州大学伯克利分校的研究团队开源。我个人觉得,它最大的贡献不是跑得快,而是把显存用到了极致。

vLLM 的核心思想很简单:像操作系统管理内存一样管理显存。你想想看,操作系统是怎么处理内存的?分页、虚拟内存、按需分配。vLLM 借鉴的就是这套思路。

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

  • 动态显存分配:不再预分配一整块,而是按需分配小单元
  • 高效缓存复用:相同前缀的请求共享 KV cache
  • 连续批处理:请求可以随时加入或退出,不阻塞

嗯,这里要注意,vLLM 不是万能的。它主要优化的是解码阶段(生成 token 的过程),预填充阶段(处理输入 prompt)的优化相对有限。但实际场景中,解码阶段占了 80% 以上的时间,所以收益非常明显。

1.3 为什么需要 vLLM?

我直接说结论:没有 vLLM,你很难把大模型推理的成本降下来。

举个例子。我之前用原生 PyTorch 部署一个 13B 模型,单卡 A100 只能同时处理 4 个请求,显存占用 75GB。换成 vLLM 后,同样的硬件,并发数提升到了 16,显存占用反而降到了 50GB。你算算,吞吐量提升了 4 倍。

为什么会差这么多?关键在于 vLLM 的连续批处理机制。传统框架必须等一个批次的所有请求都生成完毕,才能处理下一批。vLLM 不一样,它允许请求动态加入和退出。比如,一个短请求生成完了,立刻释放资源给新请求,不用等长请求。

我的建议:如果你的服务需要处理变长输入输出(比如聊天机器人、代码生成),vLLM 几乎是必选项。但如果你的场景是固定长度生成(比如批量打分),收益可能没那么大。

1.4 PagedAttention 原理简介

PagedAttention 是 vLLM 的核心创新。名字很形象——分页注意力,灵感来自操作系统的分页内存管理。

先回顾一下传统 Attention 是怎么存 KV cache 的。每个请求的 KV cache 是一块连续显存,形状是 [num_layers, num_heads, seq_len, head_dim]。seq_len 会随着生成过程不断增长,所以你必须提前预留最大长度。这就像你租房子,明明只需要住 30 平米,房东非要你租 100 平米的,剩下的空间空着也不让你用。

PagedAttention 的做法是:把 KV cache 切成固定大小的块(Block),每个块可以独立分配和释放。请求的 KV cache 不再是一整块连续内存,而是由多个块组成的逻辑页表

我画了一张图,帮你理解这个结构:

传统方式:连续显存分配 已使用 预分配但未使用(浪费) PagedAttention:分页显存分配 逻辑页表 Block 0 → 物理块 3 Block 1 → 物理块 7 Block 2 → 物理块 1 物理显存块池 块 0 块 1 ← 请求B 块 2 块 3 ← 请求A 块 4 块 5 块 6 ← 请求B 块 7 ← 请求A 绿色:空闲块 | 红色:已分配块

你看,传统方式是一整块连续内存,中间有大量空洞。PagedAttention 把显存切成小块,每个请求只占用需要的块数,而且这些块在物理上可以不连续。这就解决了碎片化问题。

更妙的是,PagedAttention 还支持块级别的内存共享。如果多个请求有相同的前缀(比如系统提示词),它们的 KV cache 可以共用同一个物理块。这在对话场景中特别有用——所有用户共享系统提示词的 KV cache,能省下 30%-50% 的显存。

曾经踩过的坑:我曾经在调优时把块大小设成了 512,结果显存碎片化反而更严重了。后来发现,块大小要根据模型层数和 head 维度来算。一般建议块大小设为 16 或 32,太小了页表开销大,太大了又回到连续分配的老路。

1.5 核心优势一览

我把 vLLM 和传统方案做了个对比,你一看就明白:

对比维度 传统方案 vLLM
显存分配 预分配连续块 按需分配小块
显存利用率 40%-60% 85%-95%
并发能力 低(受碎片化限制) 高(动态调度)
前缀共享 不支持 支持
批处理方式 静态批处理 连续批处理

1.6 什么时候该用 vLLM?

我个人的经验是,以下场景 vLLM 的收益最大:

  • 高并发在线服务:比如聊天机器人、API 网关,请求到达时间随机
  • 变长生成:输出长度从几十到几千不等,传统方案很难优化
  • 共享前缀场景:比如所有请求都带相同的系统提示词
  • 显存受限:比如你只有单卡 A100,但想跑 13B 甚至 30B 模型

反过来,如果你的场景是固定长度批量推理(比如离线打分),或者模型本身很小(比如 1B 以下),vLLM 的优势就不明显了。这时候用 Hugging Face 的 pipeline 反而更简单。

一个小技巧:初次使用 vLLM 时,建议先跑一遍 benchmark。用 vllm.entrypoints.openai.api_server 启动服务,然后用 vllm bench 压测。看看显存占用和吞吐量,再决定要不要上生产。

好了,这一章就到这里。PagedAttention 的原理其实不复杂,但它的设计思路非常值得学习——把操作系统的内存管理思想引入深度学习推理,这个跨界创新确实漂亮。


专注资料整理