大模型推理过程中,KV Cache内存消耗是制约吞吐量的核心瓶颈。传统注意力机制为每个序列预分配连续显存空间,导致大量显存碎片和浪费。PagedAttention借鉴操作系统虚拟内存分页管理思想,将KV Cache划分为固定大小的物理块(block),按需分配和回收显存,大幅降低显存浪费率,使大模型推理吞吐量提升2-4倍。本文从KV Cache内存模型出发,拆解PagedAttention的分页管理机制与vLLM调度器实现细节。
大模型推理KV Cache内存消耗模型
Transformer自注意力机制在推理时需要缓存每个token的Key和Value向量,避免重复计算。对于一个具有L层、每个注意力头维度为d_head、注意力头数为n_head的模型,单个token的KV Cache占用显存为:
2 × L × n_head × d_head × dtype_size(字节)
以Llama-2-70B为例,L=80,n_head=64,d_head=128,使用FP16精度,单个token的KV Cache约2.5MB。当batch_size=32、seq_len=2048时,KV Cache总显存消耗约160GB,远超模型权重本身的140GB。
传统KV Cache分配方式的问题
传统推理框架(如HuggingFace Transformers)为每个请求预分配最大序列长度的连续显存空间。存在三个严重问题:
1. 内部碎片:实际生成长度远小于max_seq_len时,预分配空间大量浪费,实测浪费率60%-80%
2. 外部碎片:不同请求释放后留下不连续显存块,新请求无法利用
3. 无法动态扩容:序列长度不确定时只能按最大值预分配,batch_size受限严重
PagedAttention分页内存管理原理
PagedAttention将KV Cache划分为固定大小的block,每个block存储固定数量token的KV向量。block size通常设为16,即每个block存储16个token的Key和Value。
核心数据结构包含三层映射:
1. Logical block table:逻辑块表,记录序列的逻辑块号到物理块号的映射
2. Physical block table:物理块表,记录GPU显存中实际block的分配状态
3. Block allocator:块分配器,管理空闲block池,支持分配、回收和复用
PagedAttention注意力计算流程
在注意力计算时,PagedAttention通过逻辑块表查找每个token的KV向量物理位置,将分散在物理block中的KV向量按逻辑顺序组合后进行注意力计算。CUDA kernel中实现了基于块表的索引查找逻辑:
// PagedAttention CUDA Kernel 核心逻辑(伪代码)
__global__ void paged_attention_kernel(
float* output, // 输出
const float* q, // Query向量
const float* key_cache, // KV Cache物理存储
const float* value_cache,
const int* block_table, // 逻辑到物理块映射表
int context_len, // 上下文长度
int num_heads,
int head_dim,
int block_size, // 每个block的token数
float scale
) {
int head_idx = blockIdx.x;
int seq_idx = blockIdx.y;
int token_idx = threadIdx.x;
// 查找当前token所在的物理block
int logical_block_num = token_idx / block_size;
int block_offset = token_idx % block_size;
int physical_block_num = block_table[seq_idx * max_blocks + logical_block_num];
// 从物理block中读取Key和Value
int physical_offset = physical_block_num * block_size + block_offset;
float key = key_cache[head_idx * head_dim + physical_offset * num_heads * head_dim];
float value = value_cache[head_idx * head_dim + physical_offset * num_heads * head_dim];
// 计算注意力分数
float score = dot_product(q, key) * scale;
// ... softmax和加权求和
}
vLLM调度器与PagedAttention协同工作
vLLM推理引擎基于PagedAttention实现了动态批处理调度器,支持以下机制:
1. 按需分配:新token生成时才分配新block,block大小为16时显存浪费率低于6%
2. 共享前缀:多个请求共享相同system prompt的KV Cache block,通过引用计数管理共享block,避免重复计算
3. 抢占与恢复:显存不足时,调度器将低优先级请求的KV Cache换出到CPU内存,释放GPU block给高优先级请求,待资源可用时再换回
4. Continuous Batching:与PagedAttention配合实现动态拼装batch,每个iteration可以加入新请求或移除已完成请求
PagedAttention显存节省效果实测
在Llama-2-70B模型、A100 80GB GPU上的实测数据:
传统KV Cache分配方式:batch_size=8,seq_len=2048,KV Cache占用约40GB,GPU利用率约45%
PagedMemory分配方式:batch_size=32,seq_len=2048,KV Cache占用约38GB,GPU利用率约72%
显存浪费率从传统方式的约80%降低到PagedAttention的约4%,有效batch_size提升4倍,吞吐量提升约2.4倍。
vLLM部署配置与PagedAttention参数调优
使用vLLM部署大模型时,PagedAttention相关参数通过EngineArgs配置:
from vllm import LLM, SamplingParams
llm = LLM(
model="meta-llama/Llama-2-70b-hf",
tensor_parallel_size=4, # 4卡张量并行
gpu_memory_utilization=0.90, # GPU显存利用率上限90%
max_num_batched_tokens=8192, # 单批次最大token数
max_num_seqs=256, # 最大并发序列数
block_size=16, # PagedAttention块大小
swap_space=8, # CPU换出空间(GB)
enable_prefix_caching=True, # 开启前缀缓存
)
sampling_params = SamplingParams(
temperature=0.8,
top_p=0.95,
max_tokens=512,
)
outputs = llm.generate(["请解释PagedAttention的工作原理"], sampling_params)
block_size参数影响碎片率和kernel效率:block_size越小碎片越少但kernel开销增大,block_size=16在大多数场景下取得最佳平衡。gpu_memory_utilization建议设为0.85-0.92,预留空间给CUDA context和中间张量。
PagedAttention在不同推理场景下的适用性
单序列长上下文场景(如文档摘要):PagedAttention的按需分配机制显著减少长序列的显存占用,支持超过max_position_embeddings的上下文长度。
高并发短序列场景(如聊天机器人):Continuous Batching配合PagedAttention实现高吞吐,batch_size可达数百。
多LoRA适配器场景:vLLM支持在同一batch中混合不同LoRA适配器,PagedAttention的block管理不受LoRA切换影响。
PagedAttention通过分页内存管理从根本上解决了KV Cache显存碎片问题,是大模型推理引擎的核心技术之一。理解其工作机制有助于在实际部署中合理配置显存参数,最大化推理吞吐量。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/da-mo-xing-kvcache-nei-cun-you-hua-yu-pagedattention-xian/