大模型推理的“内存刺客”:KV Cache从原理到极致优化的全景剖析

——深度剖析KV Cache的数学原理、显存爆炸根源与PagedAttention/量化/剪枝三大优化体系

一句话概括:KV Cache不是一项“可选的锦上添花”,而是Transformer自回归推理从O(n²)计算泥潭脱身的唯一出路——它用线性增长的显存换来了线性增长的计算复杂度,却也因此从“计算瓶颈的拯救者”变成了“显存容量的吞噬者”,催生了PagedAttention、KV量化、稀疏剪枝三大优化体系,成为大模型推理优化最核心的系统级战场。

2023年初,你部署了一个70B的Llama模型。输入一段128K token的文档,问了一个问题。然后,你看到显存占用从14GB一路飙升到120GB+,推理速度越来越慢,最后——OOM(显存溢出)。

你可能会问:模型权重不是才140GB吗?多出来的显存去哪了?

答案是KV Cache。

它像一位“记忆秘书”,默默记录着模型在生成过程中见过的每一个token的Key和Value。没有它,每生成一个新token,模型就得把前面所有token的注意力重新算一遍——128K token的序列,计算量是160亿次键值对运算。有了它,每次只需计算新token的K/V,计算量直降98%,推理延迟从320ms降至6ms。

看起来很完美,对吧? 一个“空间换时间”的经典交易。

但是——当上下文从4K扩展到128K、再到10M token时,KV Cache从“功臣”变成了“内存刺客”。以Llama 3.1 405B为例,单条128K token的请求,KV Cache就要吃掉约60GB显存。如果有10个并发请求,光KV Cache就得600GB——比模型权重本身还大好几倍。

KV Cache的演进,是一个典型的“优化跑步机”现象:一个为解决计算瓶颈而生的技术,因为太成功,其副作用(内存消耗)反而成了下一个更大的系统瓶颈。

本文将从数学原理、源码实现、系统管理和前沿优化四个维度,深度剖析KV Cache的完整技术图谱——它不是一行缓存代码,而是一场关于“记忆该存多少、存哪里、怎么存”的系统工程。

一、整体架构与设计哲学:KV Cache的“三问”

1.1 为什么需要KV Cache?

Transformer的核心是自注意力机制(Self-Attention) 。每个token在计算注意力时,需要与序列中所有token的Key(键)和Value(值)进行交互。

在大模型自回归生成(逐个token预测)的过程中,问题出现了:

没有KV Cache时,生成第n个token,模型需要重新计算前n-1个token的全部K/V——计算复杂度是O(n²)。

有KV Cache时,前n-1个token的K/V被缓存下来,每次只需计算当前新token的K/V,再与缓存拼接——计算复杂度降至O(n)。

序列越长,KV Cache的收益越明显。

1.2 KV Cache为什么成了“内存刺客”?

KV Cache的内存占用可以用一个公式精确计算:

KV Cache Size = 2 × L × H_kv × D × S × sizeof(dtype)

其中:

  • L = 模型层数(如Llama 3 70B有80层)
  • H_kv = KV注意力头数(GQA中KV头数少于Q头数)
  • D = 每个头的维度(如128)
  • S = 序列长度(token数)
  • sizeof(dtype) = 每个数值的字节数(FP16为2字节)

举个例子:Llama 3 70B(L=80, H_kv=8, D=128),FP16精度下,每个token的KV Cache占用约160KB。128K token的上下文,单条请求的KV Cache就是20GB。10个并发请求——200GB。

看到了吗? KV Cache的显存占用随序列长度×批处理大小线性增长。在长上下文场景下,KV Cache的显存占用常常超过模型权重本身。

1.3 两个阶段:Prefill与Decode

KV Cache的生命周期分为两个阶段:

阶段做什么KV Cache状态
预填充(Prefill)处理整个输入序列,计算所有输入token的注意力生成并填充完整的初始KV Cache
解码(Decode)逐个生成新token每步读取历史KV,追加新token的K/V

Prefill阶段是计算密集型(大量并行矩阵乘法),Decode阶段是内存带宽密集型(每次只算一个token,但需要读取全部历史KV)。这也是为什么Decode阶段的推理速度往往受限于显存带宽而非计算能力。

二、核心抽象与源码实现:从数学到代码

2.1 注意力机制的数学表达

标准自注意力计算为:

Attention(Q, K, V) = softmax(Q·K^T / √d_k)·V

在生成第t个token时:

  • Q_t:当前token的查询向量(1×d)
  • K_{1:t-1}:历史token的键矩阵((t-1)×d)
  • V_{1:t-1}:历史token的值矩阵((t-1)×d)

引入KV Cache后,每步只需计算:

α_t = softmax(Q_t · K_{1:t-1}^T / √d_k)
O_t = α_t · V_{1:t-1}

历史K/V直接复用,无需重新计算。

2.2 核心数据结构:三维缓存张量

KV Cache在内存中通常组织为四维张量:

# 文件路径:transformers/models/llama/modeling_llama.py(简化示意)

class KVCache:
    def __init__(self, max_seq_len, num_heads, head_dim, dtype=torch.float16):
        # Key Cache: [batch_size, num_heads, max_seq_len, head_dim]
        self.key_cache = torch.zeros(
            max_seq_len, num_heads, head_dim, dtype=dtype
        )
        # Value Cache: 同样形状
        self.value_cache = torch.zeros(
            max_seq_len, num_heads, head_dim, dtype=dtype
        )
        self.current_pos = 0
    
    def update(self, new_k, new_v):
        """更新缓存——只存储新增部分"""
        batch_size = new_k.size(0)
        seq_len = new_k.size(1)
        # 将新token的K/V追加到缓存中
        self.key_cache[self.current_pos:self.current_pos+seq_len] = new_k
        self.value_cache[self.current_pos:self.current_pos+seq_len] = new_v
        self.current_pos += seq_len
        return self.key_cache[:self.current_pos], self.value_cache[:self.current_pos]

这段代码实现了什么? 它实现了KV Cache最基础的数据结构和更新逻辑——为每个注意力头维护独立的K/V缓存,每次只追加新token,历史数据直接复用。

逐行解读:

  • 第5-8行:初始化K和V两个缓存张量,形状为[max_seq_len, num_heads, head_dim]
  • 第9行:current_pos记录当前已缓存的位置指针
  • 第12-17行:update方法将新token的K/V写入缓存中对应的位置
  • 第18行:返回完整的K/V缓存供注意力计算使用

设计模式解读:这里体现的是缓存模式(Cache Pattern) ——将计算密集的中间结果存储起来,后续直接读取,避免重复计算。这是计算机体系结构中最经典的“空间换时间”策略在LLM推理中的具体体现。

2.3 Decoder层的KV Cache集成

在实际的Transformer解码器层中,KV Cache的集成如下:

# 文件路径:transformers/models/llama/modeling_llama.py(简化示意)

class LlamaAttention(nn.Module):
    def __init__(self, config):
        self.num_heads = config.num_attention_heads
        self.head_dim = config.hidden_size // self.num_heads
        # Q/K/V投影层
        self.q_proj = nn.Linear(config.hidden_size, config.num_heads * self.head_dim, bias=False)
        self.k_proj = nn.Linear(config.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
        self.v_proj = nn.Linear(config.hidden_size, self.num_key_value_heads * self.head_dim, bias=False)
        self.o_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
        # ★ KV Cache缓冲区
        self.register_buffer("cache_k", None)
        self.register_buffer("cache_v", None)
    
    def forward(self, hidden_states, use_cache=True):
        # 1. 计算当前token的Q/K/V
        q = self.q_proj(hidden_states)
        k = self.k_proj(hidden_states)
        v = self.v_proj(hidden_states)
        
        # 2. 重塑为多头格式
        q = q.view(bsz, seq_len, self.num_heads, self.head_dim).transpose(1, 2)
        k = k.view(bsz, seq_len, self.num_key_value_heads, self.head_dim).transpose(1, 2)
        v = v.view(bsz, seq_len, self.num_key_value_heads, self.head_dim).transpose(1, 2)
        
        # 3. ★ 如果有KV Cache,拼接历史K/V
        if use_cache and self.cache_k is not None:
            k = torch.cat([self.cache_k, k], dim=2)
            v = torch.cat([self.cache_v, v], dim=2)
        
        # 4. 更新缓存
        if use_cache:
            self.cache_k, self.cache_v = k, v
        
        # 5. 计算注意力
        attn_output = F.scaled_dot_product_attention(q, k, v, attn_mask=causal_mask)
        return self.o_proj(attn_output)

这段代码实现了什么? 它展示了KV Cache在单个注意力层中的完整集成——新token的K/V与历史缓存拼接后一起参与注意力计算。

逐行解读:

  • 第12-13行:register_buffer注册两个缓存张量,不参与梯度计算但随模型保存
  • 第22-25行:计算当前输入的Q/K/V并重塑为多头格式
  • 第28-30行:核心逻辑——如果缓存存在,将新K/V与历史缓存沿序列维度拼接
  • 第33-34行:更新缓存供下一轮使用
  • 第37行:使用PyTorch的scaled_dot_product_attention(自动选择Flash Attention等高效实现)完成注意力计算

设计权衡(KV Cache的内存换速度) :

该设计的收益在于:
① 计算量从O(n²)降为O(n)——128K序列下计算量降低98%
② 推理延迟大幅降低——16K序列从320ms降至6ms
③ 长文本生成成为可能——没有KV Cache,超过4K的文本生成在计算上不可行

该设计的代价在于:
① 显存占用随序列长度线性增长——128K上下文下KV Cache可达60GB+
② Decode阶段受限于显存带宽——每次生成需读取全部历史KV
③ 批处理能力受限——并发请求越多,KV Cache总占用越大

三、系统级优化:PagedAttention与vLLM

你可能会问:既然KV Cache是“内存刺客”,那有没有办法让它不“刺”得那么狠?

答案是PagedAttention——vLLM的核心创新,也是目前业界最成功的KV Cache管理系统。

3.1 传统KV Cache管理的三大问题

在PagedAttention之前,KV Cache的管理方式类似于连续内存分配:

问题表现后果
内存碎片不同请求的KV Cache长度不同,分配/释放后产生碎片显存利用率仅60-70%
预分配浪费需按最大序列长度预分配显存短请求浪费大量显存
无法共享相同前缀(如系统提示词)的KV被重复计算和存储计算和显存双重浪费

3.2 PagedAttention:操作系统分页思想的移植

PagedAttention的核心思想是将KV Cache划分为固定大小的“块”(Block) ,每个块包含固定数量token的K/V。

传统方式:每个请求的KV Cache是连续的一大块内存
PagedAttention:每个请求的KV Cache被分成多个小块,散落在显存的不同位置

关键机制:

机制说明
分页存储KV Cache按块(默认16 tokens/块)存储在非连续物理内存中
按需分配需要多少分配多少,无需预分配最大长度
零碎片释放的块回归内存池,可被其他请求复用
块共享相同前缀的KV块可在多个请求间共享

设计模式解读:PagedAttention体现的是操作系统虚拟内存管理思想在深度学习推理中的直接移植——逻辑上连续的KV Cache,物理上可以分散存储,通过映射表实现寻址。

设计权衡(PagedAttention) :

该设计的收益在于:
① 显存利用率大幅提升——消除内存碎片,支持更大批处理
② 吞吐量显著提高——vLLM成为目前吞吐最高的开源推理引擎
③ 前缀共享——相同系统提示词的KV可复用,节省计算和显存

该设计的代价在于:
① 实现复杂度高——需要维护逻辑块到物理块的映射表
② 块级管理开销——分块和寻址带来额外的CPU/GPU开销
③ 共享块的引用计数管理——需要追踪每个物理块被多少个请求使用

3.3 自动前缀缓存:让“重复劳动”消失

vLLM基于PagedAttention进一步实现了自动前缀缓存(Automatic Prefix Caching) 。

核心思想:每个KV块可以通过“前缀token的哈希值”唯一标识。如果两个请求共享相同的前缀(如相同的系统提示词),它们的KV块可以映射到同一块物理内存。

缓存驱逐策略(当缓存满时):

  1. 优先驱逐引用计数为0的块(当前没有请求在用)
  2. 如果有多个,优先驱逐最近最少使用(LRU) 的块
  3. 如果访问时间相同,优先驱逐位于最长前缀末尾的块

四、精度压缩优化:量化、剪枝与架构创新

PagedAttention解决了“内存怎么管理”的问题,但没解决“内存能不能更小”的问题。量化、剪枝和架构创新,就是从源头上压缩KV Cache的体积。

4.1 KV Cache量化:从16位到4位

量化是压缩KV Cache最直接的手段——把每个数值从16位压缩到8位、4位甚至更少。

FP8 KV Cache:生产级标配

FP8是当前生产环境中最成熟的KV Cache量化方案。NVIDIA H100/H200通过张量核心原生支持FP8矩阵乘法,FP8 KV Cache已在生产环境中得到广泛应用。

NVFP4 KV Cache:Blackwell时代的4位方案

2025年12月,NVIDIA针对Blackwell架构GPU推出了NVFP4 KV Cache量化技术。相比FP8,NVFP4能将KV Cache显存占用再降低50%,实现上下文容量翻倍。在代码生成、知识问答和长上下文基准测试中,精度损失仅约1% 。

INT4/INT2:极限压缩的探索

更激进的量化方案正在涌现。CommVQ实现了2-bit KV Cache量化,将FP16 KV Cache大小压缩87.5% 。甚至1-bit KV Cache量化也已实现,精度损失控制在可接受范围内。

设计权衡(KV Cache量化) :

该设计的收益在于:
① 显存占用大幅降低——FP8降50%,INT4降75%,INT2降87.5%
② 支持更大批处理和更长上下文
③ FP8在H100上可享受硬件加速

该设计的代价在于:
① 精度损失——尤其对长上下文和数学推理任务敏感
② 需要硬件支持——FP8需要H100/H200,NVFP4需要Blackwell
③ 不同层对量化敏感度不同——需要逐层混合精度优化

4.2 KV Cache剪枝与稀疏化:不是所有token都重要

量化是把数字变短,剪枝是把数字删掉——不是所有历史token的KV都值得保留。

研究发现:注意力分数高的token更重要。基于这一洞察,SnapKV等方案选择保留注意力分数最高的top-K个token的KV,其余丢弃。

更精细的做法:不同Transformer层对KV的依赖不同。Lethe框架(AAAI 2026)在空间维度上逐层分配剪枝预算——根据估计的注意力冗余度,为每层分配不同的token保留数量;在时间维度上通过RASR(基于最近性的选择性保留) 机制进行多轮剪枝。实验结果显示吞吐量提升达2.56倍。

极限压缩:HCAttention(2026)提出了非对称K-V流水线——在GPU上进行Key评分,将Value延迟到CPU检索,将KV Cache压缩到原来的12.5%,同时保持与全注意力模型相当的精度。

设计权衡(KV剪枝) :

该设计的收益在于:
① 显存占用可降低70%以上
② 吞吐量提升2.23倍
③ 无需重新训练模型

该设计的代价在于:
① 可能丢失关键信息——尤其在需要精确回忆的任务上
② 剪枝策略的选取需要精心设计——不同任务最优策略不同
③ 增加了系统复杂度——需要动态判断哪些token值得保留

4.3 架构级创新:MLA与GQA

如果说量化和剪枝是“后天修补”,那么架构创新就是从源头减少KV Cache的“先天需求”。

GQA(分组查询注意力) :让多个Q头共享同一组KV头。Llama 3 70B中,Q头有64个,KV头只有8个——KV Cache直接缩小为原来的1/8。

MLA(多头潜在注意力) :DeepSeek-V2/V3引入的革命性方案。核心思想是将Q/K/V投影到一个紧凑的潜在空间中,而不是在高维空间直接存储。与传统MHA相比,MLA将KV Cache大小降低了一个数量级,是目前开源模型中显著减小KV Cache的最佳方法。

设计权衡(MLA) :

该设计的收益在于:
① KV Cache大幅缩减——一个数量级的压缩
② 显存带宽需求显著降低
③ 无需额外的量化或剪枝即可获得高压缩比

该设计的代价在于:
① 实现复杂度高——需要修改注意力计算的核心逻辑
② 潜在空间映射可能带来信息损失
③ 生态支持有限——目前主要在DeepSeek系列中使用

五、核心执行流程与运行时机制

5.1 完整推理链路:从输入到输出

用户输入 Prompt
    ↓
【Prefill阶段】
    ├── 模型处理全部输入token
    ├── 计算所有token的K/V
    ├── 将K/V写入KV Cache(填充)
    └── 生成第1个输出token
    ↓
【Decode阶段】循环直到生成结束
    ├── 计算当前token的Q
    ├── 从KV Cache读取全部历史K/V
    ├── 计算注意力(Q × 历史K → softmax → × 历史V)
    ├── 生成下一个token
    ├── 计算当前token的K/V
    └── 追加到KV Cache
    ↓
输出完整生成文本

5.2 KV Cache的生命周期管理

在vLLM等推理引擎中,KV Cache的生命周期由KV Cache管理器统一管理:

阶段操作说明
分配从内存池申请KV块按需分配,无需预分配最大长度
填充写入K/V张量Prefill阶段填充初始缓存
追加每步追加新token的K/VDecode阶段逐步增长
共享多个请求共享相同前缀块通过哈希表映射
驱逐缓存满时淘汰LRU策略,优先驱逐引用计数为0的块
释放请求完成后释放块块回归内存池,供其他请求复用

5.3 缓存命中率:性能的关键

KV Cache的收益高度依赖缓存命中率。高命中率时,历史K/V直接复用;低命中率时,模型不得不重新计算——回到了没有KV Cache的老路。

影响缓存命中率的因素:

  • 请求间的共享前缀长度(系统提示词越长、越固定,命中率越高)
  • 缓存容量(容量越大,能保留的块越多)
  • 驱逐策略(LRU vs 其他策略)

六、工程化实践:从部署到生产

6.1 KV Cache显存估算速查表

在实际部署前,精确估算KV Cache的显存占用是容量规划的第一步:

模型层数(L)KV头数(H_kv)头维度(D)每token KV(FP16)128K上下文
Llama 3 8B328128~130KB~16GB
Llama 3 70B808128~160KB~20GB
Llama 3.1 405B1268128~250KB~32GB
DeepSeek-V3 (MLA)61——显著更小远低于Dense模型

计算公式:每token KV = 2 × L × H_kv × D × sizeof(dtype)

6.2 优化方案选型决策树

你的部署场景是什么?

├── 你有H100/H200,追求极致吞吐
│   └── 推荐:FP8 KV Cache(无损)+ vLLM PagedAttention
│       └── 硬件原生支持,零精度损失
│
├── 你有Blackwell GPU(B100/B200)
│   └── 推荐:NVFP4 KV Cache
│       └── 显存占用再降50%,精度损失~1%
│
├── 你在消费级GPU上部署,显存紧张
│   └── 推荐:INT4/INT8 KV Cache + vLLM
│       └── 70B模型在24GB显卡上跑起来成为可能
│
├── 你处理的是超长上下文(>100K token)
│   └── 推荐:KV剪枝(SnapKV/Lethe)+ 量化 + PagedAttention
│       └── 多级压缩,逐层优化
│
└── 你从零训练或微调模型
    └── 推荐:MLA架构(如DeepSeek系列)
        └── 从源头减少KV Cache需求

6.3 常见工程陷阱与解决方案

陷阱1:忽略Decode阶段的显存带宽瓶颈

Prefill阶段是计算密集型,Decode阶段是内存带宽密集型。很多人优化了Prefill,但Decode阶段的KV Cache读取成了新的瓶颈。

解决方案:使用vLLM的PagedAttention减少内存碎片,使用量化降低KV Cache体积,减少每次读取的数据量。

陷阱2:KV Cache量化与模型量化混为一谈

模型权重量化和KV Cache量化是两件不同的事。权重量化压缩的是模型参数,KV Cache量化压缩的是推理过程中产生的中间状态。

解决方案:两者可以独立配置。例如,模型用INT4权重,KV Cache用FP8——各取所需。

陷阱3:缓存命中率被低估

在有多轮对话或相似请求的场景中,缓存命中率对性能影响巨大。低命中率时,KV Cache的收益被严重削弱。

解决方案:启用vLLM的自动前缀缓存,对系统提示词等共享前缀进行缓存;合理配置缓存容量和驱逐策略。

七、总结与展望

7.1 关键里程碑

时间里程碑意义
2017Transformer提出KV Cache的概念基础
2020sKV Cache成为LLM推理标配从学术探索到生产实践
2023vLLM + PagedAttention操作系统分页思想引入,显存利用率革命
2024GQA成为主流Llama 3等模型采用,KV Cache缩小为1/8
2024DeepSeek-V2 MLA架构级创新,KV Cache降低一个数量级
2025FP8 KV Cache生产化H100原生支持,无损量化成为现实
2025NVFP4(Blackwell)4位KV Cache,显存再降50%
2026Lethe/HCAttention层自适应剪枝+异构计算,压缩至12.5%

7.2 核心设计哲学提炼

KV Cache的演化可以用三句话概括:

  1. “从计算换时间,到内存换计算” ——KV Cache用线性增长的显存,换来了线性增长的计算复杂度,这是Transformer自回归推理的“第一性原理”

  2. “从连续分配到分页管理” ——PagedAttention把操作系统几十年的内存管理智慧搬进了GPU,让KV Cache从“显存碎片制造机”变成了“高效内存池”

  3. “从一刀切到精准打击” ——FP8/INT4量化、逐层剪枝、MLA架构创新,都在回答同一个问题:“哪些精度/哪些token值得保留,哪些可以舍弃?”

7.3 核心架构亮点速览

亮点说明效果
KV Cache数学原理缓存历史K/V,避免重复计算计算量从O(n²)降至O(n)
PagedAttention分页存储+按需分配+块共享消除碎片,吞吐量最高
FP8/NVFP4量化16位→8位/4位显存降50-75%,精度损失~1%
KV剪枝(Lethe)逐层+逐轮动态剪枝吞吐量提升2.56倍
MLA架构潜在空间压缩K/VKV Cache降低一个数量级

7.4 对开发者的启示

KV Cache的故事告诉我们:大模型推理的瓶颈,正在从“算不动”变成“存不下”。

2023年,最大的问题是“模型太大,GPU算不动”——于是有了模型量化、蒸馏、剪枝。2025年,最大的问题变成了“上下文太长,显存放不下KV Cache”——于是有了PagedAttention、KV量化、稀疏注意力。

这种“优化跑步机”现象不会停止。随着上下文从128K走向10M、100M,KV Cache的优化将成为大模型推理最核心的系统级战场。

对于开发者,这意味着:

  • 选型时关注KV Cache效率——GQA vs MHA、MLA vs MHA,差异巨大
  • 部署时优先考虑vLLM——PagedAttention是目前最成熟的KV Cache管理系统
  • 长上下文场景必须多层压缩——量化+剪枝+PagedAttention,缺一不可
  • 关注硬件趋势——FP8/H100、NVFP4/Blackwell,硬件原生支持是量产的唯一路径

最后,KV Cache的终极形态或许不是“更高效的缓存”,而是“不需要缓存”——当模型架构进化到不再需要存储全部历史K/V时(如线性注意力、状态空间模型),这个“内存刺客”才会真正退场。但在那之前,学会与KV Cache共处,是每一个大模型从业者的必修课。

本文数据来源:NVIDIA官方博客(developer.nvidia.com)、vLLM官方文档(docs.vllm.ai)、AAAI 2026/ICML 2026/ICCV 2025论文、arXiv预印本及Hugging Face Transformers源码。所有版本号、性能数据均基于公开可验证的学术论文和官方资料。

如您所在的企业正面临大模型推理优化、显存瓶颈或长上下文部署的相关需求,欢迎进一步沟通。我们可提供针对贵企业具体场景的定制化方案和现场调研服务。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐