乐于分享
好东西不私藏

vLLM 源码解析:PagedAttention 如何让大模型推理提速 10 倍

vLLM 源码解析:PagedAttention 如何让大模型推理提速 10 倍

vLLM 源码解析:PagedAttention 如何让大模型推理提速 10 倍

2024 年 UC Berkeley 的一个学生团队开源了一个项目,一年后它成为全球最流行的 LLM 推理引擎——vLLM。2026 年的 vLLM 已经是 4 万+ Star 的顶级项目。

它的核心创新只有一句话:把操作系统的虚拟内存思想用到 GPU 显存管理上。这一步,让 LLM 推理的吞吐量提升了 10-20 倍。

今天从源码角度拆解 vLLM 的 PagedAttention 核心设计。


一、背景:KV Cache 为什么是瓶颈?

大模型生成文本时,每生成一个 token,都要计算所有之前 token 的注意力。为了不重复计算,推理系统把前面 token 的 Key 和 Value 缓存下来——这就是 KV Cache

// 用户问了一句话,模型开始生成回答
// 生成的每个 token 都要重新计算注意力// KV Cache 就是用来存之前 token 的 K 和 V 的// 70B 模型,batch_size=1,输出 2048 个 token// KV Cache 占用显存 ≈ 3.5GB// batch_size=32 时 → 112GB——A100 装不下!

传统推理框架为每个请求预分配一整块连续显存,大小按最大生成长度算。但大部分请求不会生到最大长度,中间有大量显存浪费——内部碎片。而且显存块之间也有空隙——外部碎片

传统方案的问题: 预分配一整块连续显存 → 内碎片 + 外碎片 → 显存利用率仅 20-40% A100 80G,实际只能同时跑 2-3 个 70B 模型请求,剩下的 50G 全是浪费


二、PagedAttention 的核心思想

vLLM 团队问了一个问题:操作系统的虚拟内存怎么解决了物理内存碎片问题?

答案:分页。虚拟地址连续,物理地址可以不连续。按需分配物理页,缺页了再加载。

vLLM 把 KV Cache 按页管理(page_size=16 个 token),只有需要时才分配物理块:

对比维度
传统方案
PagedAttention
显存分配
预分配最大长度
按需分配页面
需要连续显存
内部碎片
大量
极少量
(仅最后一页不满)
显存利用率
20-40%
95%+
请求共享
不支持
支持页面共享

三、源码中的核心数据结构

BlockManager(页面管理器):

# vllm/core/block_manager.py
classBlockAllocator:defallocate(self, num_blocks: int):# 从空闲列表中分配 num_blocks 个物理块# 返回物理块 ID 列表         passdeffree(self, block_id: int):# 释放物理块,归还到空闲列表         passdefget_num_free_blocks(self) -> int:# 查询剩余空闲块数量return len(self.free_blocks) 

PageTable(页表——和 CPU 的页表一个概念):

# 每个请求(Sequence)维护一个页表
# 逻辑页 → 物理块 的映射 sequence.page_table = {     logical_block_0: physical_block_5,     logical_block_1: physical_block_3,     logical_block_2: physical_block_8,     ... }// KV Cache 不连续地分布在显存各处// 但页表让程序角度看它们是连续的

四、Attention 计算的源码实现

传统 Attention 需要连续的 KV Cache,PagedAttention 要处理分散在多个物理页上的 KV。关键在 Attention Kernel:

# vllm/attention/attention.py (简化)
defpaged_attention(     query,                # [num_heads, head_size]     key_cache,             # [num_blocks, block_size, num_heads, head_size]     value_cache,           # [num_blocks, block_size, num_heads, head_size]     page_table: torch.Tensor  # [batch_size, max_blocks_per_seq] ):# 1. 通过 page_table 找到 Query 对应的物理块# 2. 从物理块中读 Key 和 Value# 3. 计算 Attention Score# 4. 返回输出     ... 

实际的 CUDA kernel 实现在 vllm/attention/ops/paged_attn_kernel.cu 中。核心逻辑是:对每个 query,遍历其所有物理块,逐块计算局部 Attention,最后合并结果。这种分块计算 + 累加的模式,让非连续显存也能高效计算。


五、Copy-on-Write 和页面共享

这是 PagedAttention 另一个意想不到的收益。在生成场景中,同一个 Prompt 的不同生成请求(比如 beam search),前面对 Prompt 的 KV Cache 是完全相同的。

vLLM 利用页面共享机制:多个请求的页表指向同一组物理块。当某个请求需要写入(开始生成不同的 token)时,才触发 Copy-on-Write,分配新的物理块。

# vllm/core/block_manager.py
classCopyOnWriteBlockAllocator(BlockAllocator):defallocate_or_get_cow(self, block_id):if block_id 被多个请求共享:# 分配新物理块,复制旧数据             new_id = self.allocate(1)[0]             copy_block(block_id, new_id)return new_idelse:return block_id  # 直接复用

六、Scheduler —— 谁先跑谁后跑

vLLM 的调度器也是源码中值得看的部分。一个请求被分成多个 iteration 执行,每个 iteration 完成后检查是否还有剩余显存,有的话就插入新请求:

# vllm/core/scheduler.py (简化)
classScheduler:defschedule(self):         running = []         waiting = self.waiting_queue# 1. 先把 waiting 中的请求加入,如果显存够for req in waiting:if self.block_manager.can_allocate(req):                 running.append(req)else:break# 2. 对每个 running 请求,预分配下一页for req in running:             self.block_manager.ensure_next_page(req)return running 

注意这里的关键行为:每次 schedule 都只是预分配下一页,不预分配全部。这就是按需分配的精髓——一个请求生成到第 5 个 token 时,只分配了 1 个物理页(16 个 token 容量);如果它生成到第 17 个 token,才分配第二个页。


七、从源码学习到的设计智慧

1. 跨领域迁移 PagedAttention 没有发明新概念,只是把操作系统几十年前的分页思想用到了 GPU 显存管理上。很多优秀设计的本质就是「从另一个领域拿一个成熟方案过来」。

2. 定位核心瓶颈 vLLM 团队没有优化模型本身,而是优化 KV Cache 的存储方式。找准瓶颈比盲目优化更重要。

3. 预分配 → 按需分配 这是系统设计中反复出现的模式——不管是内存、连接池、还是线程,按需分配 + 分页总是优于预分配一大块。

4. 善用 Copy-on-Write 多请求共享相同前缀时节省大量显存,这个思路可以复用到缓存系统、文件系统中。


八、读源码的路径建议

步骤
文件
理解目标
1
论文 + docs/design
理解 PagedAttention 整体思路
2
vllm/core/block_manager.py
BlockAllocator + 页表管理
3
vllm/core/scheduler.py
请求调度 + 按需分配策略
4
vllm/worker/model_runner.py
模型推理的整体执行流程
5
csrc/attention/paged_attn_kernel.cu
CUDA Kernel 级别的 Attention 实现

从第一步到第五步,你可以完整地理解一个「从论文到工业级实现」的全过程。这是 2026 年最值得花时间读的 AI 工程源码之一。

如果觉得文章有帮助

欢迎关注公众号「随生门户」,持续输出高质量技术内容

© 2026 随生门户 · 转载请注明出处