news 2026/6/20 3:16:24

vLLM推理引擎架构:PagedAttention机制与高吞吐推理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
vLLM推理引擎架构:PagedAttention机制与高吞吐推理

vLLM推理引擎架构:PagedAttention机制与高吞吐推理

一、大模型推理的显存碎片困境:KV Cache的管理挑战

大模型推理的核心瓶颈在于KV Cache的显存管理。自回归生成过程中,模型需要缓存每一步的Key和Value向量,用于后续Token的注意力计算。KV Cache的大小与序列长度和批量大小成正比——一个70B模型处理4096长度的序列时,单个请求的KV Cache可达数GB。

传统的推理引擎为每个请求预分配一块连续的显存来存储KV Cache,这导致严重的显存碎片问题:预分配过大会浪费显存,预分配过小则序列生成到一半时需要重新分配(引发延迟尖峰);不同请求的序列长度差异大,预分配大小难以统一;请求完成后释放的显存块可能无法被新请求复用(外部碎片)。

vLLM通过PagedAttention机制,借鉴操作系统的虚拟内存分页思想,将KV Cache按固定大小的Block管理,按需分配和释放,彻底解决了显存碎片问题。

二、PagedAttention机制详解

2.1 核心思想:KV Cache的分页管理

PagedAttention将每个请求的KV Cache组织为多个固定大小的Block,每个Block存储固定数量Token的Key和Value向量。Block按需分配,不需要连续的显存空间,类似于操作系统的分页内存管理。

graph TB subgraph "传统预分配" A1[请求1: 连续显存块<br/>大量浪费空间] A2[请求2: 连续显存块<br/>空间不足需扩容] A3[请求3: 等待分配<br/>碎片无法利用] end subgraph "PagedAttention" B1[请求1: Block列表<br/>按需分配] B2[请求2: Block列表<br/>按需分配] B3[请求3: Block列表<br/>复用已释放Block] end subgraph "Block池" C[Block 0][Block 1][Block 2][Block 3] D[Block 4][Block 5][Block 6][Block 7] end B1 --> C B1 --> D B2 --> C B3 --> C

2.2 Block管理器实现

class BlockManager: """KV Cache Block管理器""" def __init__(self, num_blocks: int, block_size: int, num_heads: int, head_dim: int, dtype=torch.float16): self.block_size = block_size # 每个Block存储的Token数 self.num_blocks = num_blocks self.free_blocks = list(range(num_blocks)) # 空闲Block列表 # 预分配KV Cache的Block池 element_size = torch.tensor([], dtype=dtype).element_size() self.k_cache = torch.zeros( num_blocks, block_size, num_heads, head_dim, dtype=dtype, device='cuda' ) self.v_cache = torch.zeros( num_blocks, block_size, num_heads, head_dim, dtype=dtype, device='cuda' ) def allocate(self, num_blocks: int) -> list: """分配指定数量的Block""" if len(self.free_blocks) < num_blocks: raise OutOfMemoryError( f"Need {num_blocks} blocks, " f"only {len(self.free_blocks)} available" ) allocated = self.free_blocks[:num_blocks] self.free_blocks = self.free_blocks[num_blocks:] return allocated def free(self, block_ids: list): """释放Block回空闲池""" self.free_blocks.extend(block_ids) def get_block_table(self, request_id: str) -> list: """获取请求的Block映射表""" return self.request_block_map.get(request_id, [])

2.3 PagedAttention Kernel

PagedAttention的核心挑战是:注意力计算需要访问KV Cache,但KV Cache不再存储在连续的显存地址中,而是分散在多个Block中。需要自定义CUDA Kernel实现跨Block的注意力计算。

class PagedAttentionFunction(torch.autograd.Function): """PagedAttention前向传播""" @staticmethod def forward(ctx, query, key_cache, value_cache, block_tables, context_lens, block_size): """ Args: query: [num_tokens, num_heads, head_dim] key_cache: [num_blocks, block_size, num_heads, head_dim] value_cache: [num_blocks, block_size, num_heads, head_dim] block_tables: [num_seqs, max_num_blocks_per_seq] context_lens: [num_seqs] 每个序列的实际长度 block_size: 每个Block的Token数 """ num_tokens = query.shape[0] num_heads = query.shape[1] head_dim = query.shape[2] output = torch.empty_like(query) # 调用自定义CUDA Kernel # 核心逻辑:根据block_tables找到每个Token对应的KV Block # 在Block内计算注意力分数,跨Block累加 paged_attention_kernel( output, query, key_cache, value_cache, block_tables, context_lens, block_size ) return output

三、连续批处理与调度

3.1 连续批处理(Continuous Batching)

传统推理引擎采用静态批处理——等待所有请求完成后才开始下一批。vLLM采用连续批处理(Iteration-level Scheduling),每次迭代都重新调度:已完成的请求移出,新请求加入,无需等待同批其他请求完成。

class Scheduler: """请求调度器""" def __init__(self, block_manager: BlockManager, max_num_seqs: int = 256): self.block_manager = block_manager self.max_num_seqs = max_num_seqs self.waiting_queue = [] # 等待队列 self.running_seqs = [] # 正在运行的序列 self.max_seq_len = 8192 def schedule(self) -> SchedulerOutput: """每次迭代调度:决定哪些序列参与本轮计算""" scheduled_seqs = [] # 1. 保留正在运行的序列 for seq in self.running_seqs: if seq.is_finished(): # 序列已完成,释放Block self.block_manager.free(seq.block_table) else: # 检查是否需要分配新的Block if self._need_new_block(seq): new_block = self.block_manager.allocate(1) if new_block: seq.block_table.extend(new_block) scheduled_seqs.append(seq) else: # 显存不足,抢占(Preemption) self._preempt(seq) else: scheduled_seqs.append(seq) # 2. 从等待队列中添加新序列 remaining_slots = self.max_num_seqs - len(scheduled_seqs) for _ in range(remaining_slots): if not self.waiting_queue: break seq = self.waiting_queue.pop(0) # 为新序列分配初始Block num_blocks = math.ceil( seq.prompt_len / self.block_manager.block_size) blocks = self.block_manager.allocate(num_blocks) if blocks: seq.block_table = blocks scheduled_seqs.append(seq) else: # 显存不足,放回等待队列 self.waiting_queue.insert(0, seq) break self.running_seqs = scheduled_seqs return SchedulerOutput(running_seqs=scheduled_seqs) def _preempt(self, seq): """抢占策略:释放低优先级序列的Block""" # 通过重新计算(Recomputation)而非交换(Swapping)恢复 self.block_manager.free(seq.block_table) seq.block_table = [] seq.num_computed_tokens = 0 # 需要重新计算 self.waiting_queue.append(seq)

3.2 抢占与恢复策略

当显存不足时,调度器需要抢占(Preempt)部分运行中的序列,释放其Block给更高优先级的序列。vLLM采用重新计算策略——被抢占的序列放回等待队列,重新执行Prompt阶段恢复KV Cache。

四、架构权衡与边界分析

4.1 Block大小的选择

Block过小(如1个Token),Block表的管理开销增大,注意力Kernel的访存效率降低;Block过大(如256个Token),预分配浪费增加,短序列的显存利用率下降。vLLM默认Block大小为16,在管理开销和利用率之间取得平衡。

4.2 抢占策略的开销

重新计算策略在抢占频繁时会浪费大量计算资源。当系统过载时,抢占-恢复的循环可能导致整体吞吐量下降。建议设置合理的最大并发序列数,避免过度调度。

4.3 前缀缓存的复用

多个请求可能共享相同的System Prompt前缀,PagedAttention支持将共享前缀的KV Cache Block标记为只读,多个请求复用同一组Block,显著降低显存占用和重复计算。但前缀匹配的检测逻辑增加了调度器的复杂度。

五、总结

vLLM通过PagedAttention机制将KV Cache按Block管理,按需分配和释放,解决了传统推理引擎的显存碎片问题。连续批处理实现了迭代级调度,提升了GPU利用率。抢占与恢复策略在显存紧张时保障系统稳定性。

落地建议:从默认Block大小16开始,根据实际序列长度分布调整;监控抢占频率,高频抢占时降低最大并发数;启用前缀缓存复用共享System Prompt,但需评估匹配检测的额外开销。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/14 7:44:59

AI搜索获客实测:哪些公司靠谱?案例复盘

行业痛点分析在AI搜索重塑企业获客规则的背景下&#xff0c;中小企业正面临三重核心挑战&#xff1a;流量迁移导致传统投入失效、技术断层引发转型焦虑、市场混乱加剧试错风险。技术断层尤为突出。当企业采购决策转向AI问答时&#xff0c;传统SEO的关键词排名逻辑彻底失效。测试…

作者头像 李华
网站建设 2026/6/14 6:13:15

NestJS 别用 Express 了!Fastify + Nacos 打造配置实时推送

本文基于 NestJS 11 Fastify 5 Nacos v3 API。项目代码见文末仓库链接。一、为什么微前端需要一个 BFF 前端产出的 HTML、JS、CSS 都是静态文件。部署到服务器后&#xff0c;没有运行时读取环境变量或配置文件的能力&#xff08;不像 Node.js 后端&#xff09;。 但微前端的子…

作者头像 李华
网站建设 2026/6/14 6:43:48

比Everything还快?Listary(文件搜索工具)

软件获取地址 文件搜索类工具合集 软件介绍 Listary 是一款广受好评的 Windows 文件增强工具&#xff0c;核心优势&#xff1a; ✅自定义快捷键、搜索规则、排除目录 ✅收藏常用文件夹&#xff0c;一键直达高频工作目录 ✅低内存低 CPU 占用&#xff0c;后台静默运行不卡…

作者头像 李华
网站建设 2026/6/14 6:13:16

基于1-Wire总线与DS2405的嵌入式地址开关设计实战

1. 项目概述在嵌入式系统开发中&#xff0c;如何用最少的硬件资源实现可靠的设备寻址与控制&#xff0c;一直是个既基础又关键的挑战。尤其是在一些需要多点控制、分布式部署的场景&#xff0c;比如智能楼宇的灯光分区、工业现场的传感器节点选通&#xff0c;或者老旧设备的智能…

作者头像 李华
网站建设 2026/6/14 6:13:14

5分钟学会VRCT:VRChat全球社交无障碍的终极指南

5分钟学会VRCT&#xff1a;VRChat全球社交无障碍的终极指南 【免费下载链接】VRCT VRCT(VRChat Chatbox Translator & Transcription) 项目地址: https://gitcode.com/gh_mirrors/vr/VRCT 你是否曾因为语言障碍而在VRChat中错过精彩对话&#xff1f;是否想与日本、韩…

作者头像 李华