大语言模型推理中的 KV 缓存已成为内存瓶颈的核心来源。随着序列长度增长,KV 缓存的内存占用线性膨胀,甚至在长上下文场景下超过模型权重本身的消耗。本文聚焦内存布局优化的三大技术支柱 —— 分页策略、量化压缩与动态驱逐机制,从原理到工程实践提供可落地的优化方案。
KV 缓存的内存困境
在自回归生成过程中,模型需要为每个已生成 token 存储对应的 Key 和 Value 张量,以避免重复计算。对于包含 L 层、H 个注意力头、每头隐藏维度为 d 的模型,序列长度为 S 时的 KV 缓存内存需求为:2 × S × L × H × d × 2 字节(FP16 精度)。以 Llama-3.1-8B 为例,处理 32K 上下文时,仅 KV 缓存就需占用超过 20GB 显存。
传统方案采用预分配连续内存的方式,这导致严重的内部碎片(over-reservation)和外部碎片(scattered gaps)问题。当不同请求的序列长度差异较大时,内存利用率急剧下降,大量显存被闲置却无法被其他请求复用。
PagedAttention 分页策略
vLLM 提出的 PagedAttention 借鉴操作系统虚拟内存管理思想,将 KV 缓存划分为固定大小的逻辑块(block),通过块表(block table)映射到物理内存位置。这种设计带来三个关键优势:
按需分配:仅在 token 生成时才分配对应的 KV 块,避免预分配造成的内存浪费。每个序列的 KV 缓存由多个非连续的块组成,块表维护逻辑块到物理块的映射关系。
碎片消除:固定大小的块允许内存分配器高效复用释放的块,将内存浪费控制在极低水平(通常低于 5%)。
灵活扩展:序列增长时动态追加新块,无需重新分配连续内存空间,支持变长序列的高效处理。
PagedAttention 的块大小(block_size)是一个关键调参点。较小的块(如 8 或 16 个 token)可减少内部碎片,但会增加块表管理开销;较大的块(如 32 或 64 个 token)降低管理成本,但可能浪费块内未使用的槽位。vLLM 官方推荐 16 作为平衡点。
PagedEviction 块级驱逐机制
传统 KV 缓存压缩方法(如 H2O、StreamingLLM)存在两个与 PagedAttention 不兼容的问题:一是依赖 FlashAttention 不返回的注意力分数,二是跨页驱逐 token 会重新引入内存碎片。
PagedEviction 针对 PagedAttention 的块结构设计了结构化驱逐策略,核心创新包括:
块对齐压缩:驱逐操作以完整块为单位执行,保持块大小的一致性,避免产生部分填充的 "脏块"。在解码阶段,仅当最新块填满时才触发一次块级驱逐,而非每步驱逐单个 token。
无注意力分数的重要性评估:由于 FlashAttention 不暴露注意力权重,PagedEviction 采用 Value 张量与 Key 张量 L2 范数之比作为 token 重要性代理:S_i = ||V_i||₂ / ||K_i||₂。该指标基于 Key 范数与累积注意力分数负相关的观察,无需修改 CUDA 内核即可计算。
双阶段策略:在 Prefill 阶段执行 token 级压缩,计算每个 token 重要性后驱逐低分 token 至预算上限;在 Decode 阶段执行块级驱逐,当当前块满时评估所有现有块的平均重要性,驱逐得分最低的完整块。
实验数据显示,在 1024 token 缓存预算下,PagedEviction 在 Llama-3.2-1B 上达到 3020 tokens/sec 的吞吐,相比全缓存(2200 tokens/sec)提升 37%,同时 GovReport 数据集 ROUGE 得分 24.5,优于 StreamingLLM(21.0)和 KeyDiff(21.2)。
量化压缩的协同效应
量化技术与分页策略形成互补优化。将 KV 缓存从 FP16 压缩至 INT8 或 INT4,可在保持模型精度的同时减半或四分之一的内存占用。当量化与 PagedAttention 结合时:
- 单块存储更多 token,等效增大块容量
- 相同显存预算下支持更长上下文或更大批处理
- 块交换(swap)至 CPU 内存时的带宽压力降低
实践中,8-bit 量化通常对模型精度影响可忽略,而 4-bit 量化需针对具体任务验证。量化与分页的联合配置需重新评估 gpu_memory_utilization 参数,因为量化后的有效内存容量已发生变化。
生产环境配置清单
基于上述技术,生产环境部署 vLLM 时可参考以下参数配置:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| block_size | 16 | 平衡碎片控制与管理开销 |
| gpu_memory_utilization | 0.85-0.95 | 预留空间给 CUDA 内核与显存碎片 |
| max_num_seqs | 根据批处理需求设定 | 分页机制支持更高并发 |
| swap_space | 4-8 GB | CPU 内存用于块交换的缓冲区 |
| quantization | fp16 → int8/int4 | 根据精度要求选择 |
| cache_budget | 1024-2048 | 启用 PagedEviction 时的预算上限 |
监控层面应重点关注:KV 缓存分配率(allocated vs. reserved)、块驱逐频率、CPU-GPU 交换带宽利用率,以及长序列请求的 P99 延迟。当观察到频繁的块驱逐或交换活动时,表明缓存预算设置偏紧,需考虑扩容或进一步压缩。
局限与权衡
块级驱逐策略虽保持内存布局规整,但代价是可能丢弃块内的高重要性 token。在极端长上下文场景(如 100K+ tokens),需权衡块大小与驱逐粒度。此外,基于 Key/Value 范数的重要性代理虽避免内核修改,但在某些任务上可能与真实注意力分布存在偏差,建议针对特定领域数据校准驱逐阈值。
资料来源
- PagedEviction: Structured Block-wise KV Cache Pruning for Efficient Large Language Model Inference (arXiv:2509.04377)
- vLLM Documentation & PagedAttention Implementation
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。