4. vLLM核心架构
在推理引擎的演进过程中,vLLM之所以能实现接近线性的吞吐量提升,核心并不在于某个单一的算子优化,而在于它对显存管理范式的重构。传统推理框架将KV Cache视为连续的大块显存,而vLLM则借鉴操作系统虚拟内存的分页思想,将KV Cache切分为固定大小的块(Block),配合连续批处理(Continuous Batching)机制,让GPU资源利用率产生质的飞跃。本节将拆解这三个核心组件的协同原理。
4.1 PagedAttention:从连续显存到分页显存
传统Attention实现中,每个请求的KV Cache需要预先分配一块连续的显存区域,大小由最大序列长度决定。这带来两个严重问题:内部碎片(实际序列短于最大长度,多余显存被浪费)和外部碎片(不同请求的显存区域无法灵活拼接,导致显存利用率通常只有40%~60%)。
PagedAttention将KV Cache划分为固定大小的逻辑块(默认Block Size为16个),每个块存储16个的Key和Value张量。物理显存中,这些块可以分散存放,通过块表(Block Table)记录逻辑块到物理块的映射关系。Attention计算时,GPU线程束(Warp)按块粒度并行读取KV数据,而非按整段连续内存读取。
# 逻辑视角:每个序列的KV Cache由若干逻辑块组成
seq_1: [Block 0] -> [Block 1] -> [Block 2] (逻辑连续)
↓ ↓ ↓
# 物理视角:块表映射到任意物理位置
Block Table: [物理块7] [物理块3] [物理块12] (物理离散)
这种设计使得显存分配粒度从“整个序列”缩小到“16个”,碎片率大幅下降。更重要的是,显存共享成为可能——多个序列可以共享同一个物理块(例如并行采样时多个输出序列共享相同的Prompt前缀),这是传统连续显存方案无法实现的。
4.2 KV Cache管理机制
vLLM的KV Cache管理遵循“按需分配、用完即还”的原则,由显存分配器(Memory Allocator)统一调度。每个请求在Prefill阶段(首次计算)根据实际输入长度分配所需块数;在Decode阶段(逐生成)每生成一个新,若当前块已满则追加分配一个新块。当请求完成时,其占用的物理块被释放回全局空闲池。
管理流程包含三个关键操作:
- 分配(Allocate):从空闲块池中取出物理块,更新块表映射。分配操作的时间复杂度为O(1),因为只需移动空闲链表指针。
- 释放(Free):将物理块归还空闲池,并清除块表对应条目。若多个序列共享同一物理块,则采用引用计数——仅当引用计数归零时才真正释放。
- 拷贝(Copy-on-Write):当共享块需要被某个序列独立修改时(例如某个序列生成了不同的分支),触发写时复制,为修改方分配新物理块。
4.3 连续批处理(Continuous Batching)
传统静态批处理(Static Batching)中,一个批次的所有请求必须全部完成后才能开始下一批次。这导致排队延迟——先完成的请求必须等待慢请求,GPU算力被白白浪费。连续批处理打破了这一限制:请求级调度允许新请求随时加入当前批次,已完成的请求立即退出并释放显存。
调度器(Scheduler)在每个迭代步(Iteration)执行以下逻辑:
- 检查当前批次中哪些请求已生成结束(EOS),将其移出批次并释放KV Cache块。
- 从等待队列中取出新请求,执行Prefill计算,为其分配KV Cache块。
- 将新请求加入当前批次,与旧请求一起执行Decode步骤。
- 重复上述过程直到所有请求完成。
这种机制使得GPU始终在处理“活跃请求”,消除了批间空隙。配合PagedAttention的细粒度显存管理,调度器可以精确控制每个请求的显存占用,避免因显存不足而拒绝服务。
4.4 三者协同的完整流程
以一个实际推理场景为例:假设GPU显存为40GB,KV Cache可用20GB,Block Size=16,每个物理块占用约2MB。
| 阶段 | 操作 | 显存变化 |
|---|---|---|
| 请求到达 | 调度器分配逻辑块,映射到物理块 | 空闲池减少N块 |
| Prefill | 计算完整输入序列的KV,写入分配的块 | 块表建立映射 |
| Decode迭代 | 每生成1个,检查当前块容量 | 块满则追加分配1块 |
| 请求完成 | 释放所有物理块,引用计数减1 | 空闲池增加N块 |
| 新请求加入 | 复用刚释放的物理块 | 空闲池减少,无碎片 |
整个过程中,PagedAttention提供显存层面的灵活性,连续批处理提供时间层面的调度效率,而KV Cache管理则是连接两者的桥梁。三者缺一不可:没有分页显存,连续批处理无法高效回收碎片化显存;没有连续批处理,分页显存也无法转化为实际的吞吐量提升。
4.5 常见误区与调优建议
max_num_seqs和max_seq_len_to_capture参数限制单批请求数。
调优实践中,建议通过vllm serve启动时监控以下指标:gpu_cache_usage_percent(显存利用率)、num_requests_running(运行中请求数)、num_requests_waiting(等待请求数)。当显存利用率长期低于70%时,可增大max_num_seqs或gpu_memory_utilization参数;当等待队列持续堆积时,则需检查是否存在长请求阻塞调度。
理解这三个核心组件的协同关系,是后续优化长序列推理性能的基础。PagedAttention解决了“显存放不下”的问题,连续批处理解决了“GPU闲不住”的问题,而KV Cache管理则确保两者在动态变化中保持高效运转。