news 2026/10/25 7:59:28

KV Cache 是什么?一文搞懂大模型推理显存占用与优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
KV Cache 是什么?一文搞懂大模型推理显存占用与优化

手头那个 7B 模型,同样一张 24GB 显卡,训练时跑得好好的,一到上线做推理,并发一上来直接 OOM。相信很多人第一次遇到这个问题时都是一脸懵——代码没变,模型没变,怎么推理反而比训练更吃显存?

答案就藏在三个字母里:KV Cache。它既是让大模型推理速度快起来的关键,也是显存消耗的大头。这篇文章把这层窗户纸捅破:KV Cache 到底是什么、为什么只需要缓存 K 和 V 而不是 Q、它跟上下文长度、lost in middle 现象、以及各家 API 厂商宣传的 prompt 缓存到底什么关系。最后给出一套实操层面的显存估算和调优方案。无论你是刚入门想搞懂原理,还是已经在一线做部署、做长上下文优化,这篇都能给你一些用得上的东西。

1. 从一次推理请求看显存到底去哪了

1.1 自注意力机制的计算过程

要理解 KV Cache,绕不开 Transformer 里最核心的自注意力机制。这个机制的本质,就是让序列里的每个 token 都能“看到”它之前的所有 token,通过计算两两之间的相关度,来决定谁的贡献更大、谁的贡献更小。

具体分三步:

  • 每个 token 的隐状态向量分别乘以三个权重矩阵,得到 Query(查询向量)、Key(键向量)、Value(值向量);
  • 用当前 token 的 Q 去和序列里所有 token 的 K 做点积,得到注意力分数,再经过 Softmax 归一化成权重;
  • 拿这些权重去加权求和所有 token 的 V,得到当前 token 的输出向量。

这个过程在训练和推理中都要执行,但两者最大的差别在于:训练是并行计算整个序列,推理是逐个生成 token。

训练时,模型拿到的是完整的一句话或一篇文章,所有 token 的位置从一开始就是已知的。注意力计算可以在一个矩阵乘法里并行完成,一次性算出所有位置之间的两两注意力分数,效率很高。

但推理不一样。大模型生成文本是自回归的:先根据 prompt 预测第一个 token,再把第一个 token 拼到 prompt 后面预测第二个 token,然后继续拼、继续预测。每一步都只能生成一个 token,前面的内容不会改。这就带来一个直观的问题——如果每一步都重新把整个序列从头到尾算一遍自注意力,那生成第 100 个 token 时,前 99 个 token 的 K 和 V 会被重复计算 99 次,这种冗余计算量完全不可接受。

1.2 训练和推理的差异决定了缓存策略

打个生活化的比方。训练像是老师批改整套试卷,所有答案一起看,批改速度很快;推理像是学生一道题一道题往下做,每做一道新题都要把前面所有题目的题干和解答过程重新读一遍。如果这个学生不做笔记,每做一步都把整本卷子从头看一次,那做题速度会随着题目数量直线下降——这正是大模型推理没有 KV Cache 时的处境。

而 KV Cache 的做法,就是让模型在推理过程中边算边记:每生成一个新的 token,就把这个 token 对应的 K 向量和 V 向量存到缓存里。下一次要生成新 token 时,只需要计算新 token 的 Q,然后拿这个 Q 去和缓存里已有的所有 K、V 做注意力计算就行了,不用重新计算历史 token 的 K 和 V。

这一步优化带来的收益是数量级的。没有 KV Cache 时,生成第 N 个 token 的计算量近似是 O(N²),有了缓存后变成 O(N),并且越到后面差距越大。一个 128K 上下文的场景,两者计算量差出几千倍都不夸张。

1.3 为什么不缓存 Q,Q 去哪了

到这里,细心的朋友会发现一个特别自然的疑问:既然要缓存 K 和 V,为什么把 Q 丢了?为什么不是 QKV Cache 三件套一起存?

原因其实不复杂。在自回归推理中,历史 token 的 Q 已经完成了使命——它们当时被用来计算注意力分数、加权求和得到输出,然后这个输出进入了后续的 FFN、LayerNorm,成为下一层网络的输入。之后新的 token 进来时,历史 token 的 Q 不会再被任何计算引用。注意力计算只会用到当前 token 的 Q 来查询历史 token 的 K,也就是说,Q 是一次性的,而 K 和 V 是要反复被查询的。

更重要的是,训练阶段之所以保留 Q,是因为反向传播需要用到所有中间张量来算梯度;推理阶段不做反向传播,Q 用完就可以扔掉,完全没有保留的必要。缓存一份 Q 不但白白浪费显存,还会拖慢访存速度,纯属负优化。所以业界约定俗成叫 KV Cache,而不是 QKV Cache,不是疏忽,而是有充分理由的。

2. KV Cache 的存储结构与显存账本

2.1 我们到底在显存里存了什么

KV Cache 不是在显存里随便找个地方塞几个矩阵,它的结构跟模型架构严格对应。一个 7B 规模的 decoder-only 模型一般有 30 到 40 个 Transformer 层,每一层都有一组独立的 K、V 缓存。

具体看一个典型配置:假设模型有 L 层,每层有 H 个 KV 头,每个头的维度是 D,已经生成的 token 数是 S。那么这一层要缓存的 K 矩阵和 V 矩阵,各自的形状都是 [S, H, D](或者按推理框架实际排布,可能是 [H, D, S],逻辑上等价)。L 层叠加起来,KV Cache 的体量就是 L × 2 × S × H × D 个元素。

注意这里的 H 不一定是模型全部的注意力头数。很多新模型用了 GQA(分组查询注意力)或 MQA(多查询注意力),让多个 Q 头共享同一组 K、V 头,这会显著减少 KV Cache 的体量。比如 Llama 2 7B 是 32 个 Q 头、8 个 KV 头,KV Cache 只相当于全 MHA 方案的 8/32,也就是四分之一。

这里顺便纠正一个常见误解:KV Cache 跟模型权重一样,也是按精度占空间的。FP16(即 BF16)下一个元素占 2 字节,FP8 占 1 字节,INT4 占 0.5 字节。权重可以量化,KV Cache 同样可以量化,后面实操部分会详细讲。

2.2 显存计算公式和实际数字

KV Cache 的显存占用可以写成一个非常透明的公式:

显存字节数 = L × 2 × 精度字节数 × S × H_kv × D

这个公式不绕,我拿 Llama 2 7B 算一遍给你看。它的参数是 L=32,H_kv=8,D=128,精度按 2 字节算:

  • 每个 token 单层单头的 K 和 V 合计:2(K、V 两个矩阵)× 2 × 128 = 512 字节,再乘 8 个 KV 头 = 4KB,这是一层一个 token 的占用;
  • 一层 4096 个 token 就是 4KB × 4096 = 16MB;
  • 32 层加起来 512MB。

也就是说,一个 7B 模型在 4096 上下文下,KV Cache 大约吃掉 512MB 显存。这还只是单条序列。跑在线服务时通常要同时处理多用户请求,如果并发 8 路,那就是 4GB。

我们再把上下文拉长看一组直观数字。同样是这个 7B 模型,如果上下文从 4K 拉到 32K,KV Cache 直接涨 8 倍到 4GB;拉到 128K,就是 16GB。这时你会立刻明白,为什么很多宣称支持超长上下文的模型,本地部署之后实际能跑的长度远不如宣传——显存根本装不下那么大的缓存区。

2.3 上下文长度、并发数和 KV Cache 的三角关系

KV Cache 的显存成本和上下文长度是严格线性关系,和并发路数也是严格线性关系。这意味着什么?意味着** KV Cache 的总显存占用 = 单路 KV Cache × 并发路数**,这两者同时增长时,占用成乘法放大。

我实际部署过一个 13B 模型,权重部分用 INT8 量化后大概 14GB,单卡 24GB 看起来挺富裕。但当我试图跑到 8192 上下文加 4 路并发时,KV Cache 直接吃掉 8GB 左右,加上权重、中间激活值、CUDA 上下文开销,连模型带缓存就开始贴到 23GB 的警戒线了。后来把并发降到 2,上下文保持 8192,才稳定下来。

这里需要特别提醒的是,很多框架在初始化时就会给 KV Cache 预留显存。你现在用的推理框架可能有个gpu_memory_utilization之类的参数,它控制的就是给权重之外预留多少空间,其中很大一部分是预留给 KV Cache 的。如果框架不够聪明,预留得太多,模型根本加载不进来;预留太少,序列稍微长一点就开始 OOM。这个参数的调法,实操部分展开。

3. 容易被搞混的三个概念:QKV、lost in middle、prompt 缓存

3.1 为什么是 KV Cache,不是 QKV Cache

前面讲过 Q 不需要缓存的技术原因了,这里再从一个更工程的角度补一刀。很多刚接触的人会把 Transformer 里的 QKV 三个矩阵当成某种“三个一组必须同时出现”的东西,认为只缓存 KV 是一种不完整的方案。

实际上,自注意力计算里 Q 的角色是“发起查询的人”,K 和 V 的角色是“被查询的数据库”。推理时每来一个 token,只有 Q 是新的,它需要跑到数据库里做一次近似检索;数据库自然要保留,而之前的查询请求记录没有保留价值。这个逻辑放到任何检索系统里都成立。

再说训练层面,那套完整保留 Q 的机制是服务于反向传播的,属于训练基础设施的一部分,跟 KV Cache 要解决的是完全不同的问题。推理框架如果还打着 QKV Cache 的招牌做缓存,那是没搞懂这层区别,要么是显存浪费,要么是在玩概念。

3.2 KV Cache 与 lost in middle 的关联

lost in middle 是长上下文场景里一个非常著名的现象:当给模型输入很长的上下文时,它对开头和结尾的内容利用得很好,但中间部分经常被“视而不见”,就像一个人读长文只记住了开头和结尾。

这个现象跟 KV Cache 确实有关系,但要说得准确一点:KV Cache 只是存储注意力计算所需 KV 的机制,本身不直接导致模型“记不住中间”。真正的原因在注意力分数分布上。长上下文中,模型每个新生成的 token 都要对全部历史位置做 Softmax,而位置编码(RoPE 等)的引入会让注意力天然更偏向某些区域;加上很多模型在训练时使用了滑动窗口或截断策略,中间段落在预训练阶段就没被完整学习过。结果就是,注意力权重在长序列里分布得并不均匀,头部和尾部得分更高,中间位置的 V 被平均之后,贡献被稀释了。

但 KV Cache 确实为这个问题的研究提供了观察窗口。因为有了 KV Cache,我们可以在推理时直接检查每个位置的注意力分数分布,看到底哪些 token 拿到了高权重,哪些被忽略了。很多长上下文优化工作,比如注意力重排、关键值增强、位置插值等,都是基于 KV Cache 上观测到的注意力异常来做的。

3.3 prompt 的 cache(前缀缓存)和 KV Cache 是什么关系

市面上 API 厂商聊的“prompt 缓存”或者叫“上下文缓存”,本质上是 KV Cache 的一种应用层玩法,但很多人会把两者混为一谈。

KV Cache 本身是自回归推理中每一步都会持续增长的缓存,它随着生成 token 的增多不断变长。而前缀缓存(Prefix Caching)是这么个思路:用户请求的 prompt 往往有大量重复前缀,比如固定的 System Prompt、工具定义、长文档开头。这些前缀如果每次都重新计算一遍 KV,既浪费算力又浪费时间。于是可以在服务端按内容哈希把前缀对应的 KV Cache 存起来,下次来一个带相同前缀的新请求,直接复用存好的 KV,只对新出现的部分从头算。

这个技术说白了就是用空间换时间的另一个维度——KV Cache 是从【时序】上复用历史计算结果,前缀缓存是从【请求间】复用相同内容的计算结果。在实际工程里两者是叠加使用的:前缀缓存负责命中重复前缀,KV Cache 负责在生成过程中持续维护。像 SGLang 的 RadixAttention、vLLM 的自动前缀缓存,还有各个云厂商 API 里的上下文缓存计费项,本质都是这层东西。

3.4 KV Cache、模型权重和中间激活值的显存分工

部署大模型时,显存里最少有三类东西在抢地盘:模型权重、中间激活值(包括临时张量)、KV Cache。

模型权重是最直观的,模型文件多大,加载进来基本就占多大。中间激活值是前向计算过程中产生的临时张量,包括注意力分数、FFN 中间状态等,它在推理时被反复创建和销毁,不是常驻内存的大头。KV Cache 则是一个常驻的、随序列长度递增的结构。

有人一说显存占用就把锅全甩给模型权重,这是不准确的。很多场景下 KV Cache 和激活值加起来可能比权重更能决定能跑多大并发和上下文。比如 7B 模型 FP16 权重 14GB,但 128K 上下文下光 KV Cache 就要 16GB,权重也不如缓存吃得多。理解这个分工,你才能在做显存预算时列清楚账,而不是只盯着权重大小。

4. 推理框架中的 KV Cache 配置实操

4.1 主流推理框架中的 KV Cache 相关参数

现在主流的推理框架,从 vLLM、SGLang 到 Ollama、llama.cpp、TensorRT-LLM,几乎都暴露了跟 KV Cache 相关的可调参数,只是叫法五花八门。这里列几个最常见的,方便你对照排查:

框架常见参数作用
vLLM--max-model-len、--gpu-memory-utilization、--max-num-seqs前者限制总序列长度,中间控制 KV Cache 显存占比,后者限制并发序列数
OllamaOLLAMA_CONTEXT_LENGTH(num_ctx)、OLLAMA_NUM_PARALLEL分别控制上下文长度和并行请求数,直接决定 KV Cache 大小
llama.cpp--ctx-size、--parallel上下文长度和并行序列数
TensorRT-LLM--max_seq_len、--max_batch_size、--kv_cache_free_gpu_mem_fraction序列长度、批量大小和 KV Cache 可用显存比例

这里有一个通用的关系链:KV Cache 大小 ≈ f(上下文长度) × 并发数,所以你在任何框架里调这三个方向的参数,本质都是在给 KV Cache 做预算。上下文长度设得越长,能开的并发就越少;并发开得越多,单路上下文就必须缩短。两者都想拉满,只能换更大的卡,或者对 KV Cache 下手。

4.2 显存估算实例:7B、13B、70B 各跑多长才合适

直接上实例。我用公开的模型配置按公式逐个算过一轮,整理成一张表,精度按 FP16、KV Cache 用 GQA 方案估算,你看个量级即可:

模型层数KV 头数头维4K 单路 KV8K 单路 KV32K 单路 KV
Llama 2 7B328128512MB1GB4GB
Llama 2 13B408128640MB1.25GB5GB
Llama 3 70B8081281.25GB2.5GB10GB

参考一下权重占用量,7B FP16 约 14GB,13B 约 26GB,70B 约 140GB。所以跑 70B 这种级别的模型时,KV Cache 用满 32K 加上权重需要的显存,几乎可以吃掉一整张 A100 80G 还多,这也是为什么长上下文推理一般都要配张量并行或多机多卡。

实际操作中,我给一个经验值:单卡能跑通的最长上下文,按总显存减掉权重占用后,再留至少 15% 的余量,剩下的都分给 KV Cache 和激活值。比如一张 24GB 卡跑 7B INT8 量化(权重约 7GB),剩余约 17GB,如果目标并发 4 路,每路能分到约 3.5GB(去掉激活和 CUDA 开销),按表里 8K 单路 1GB 算,8K 上下文跑 4 并发是稳的,但 32K 单路 4GB 就超预算了,只能降并发。

4.3 减小 KV Cache 的四个实用策略

如果预算不够,就要动手压缩 KV Cache。我从“效果最明显”到“代价最小”排一个梯队:

第一梯队是换架构或头配置。GQA、MQA 这类设计直接把 KV 头数打个几折,KV Cache 体量同比缩小。你选开源模型时,优先看有没有 GQA,这是最省事的办法。MHA 老架构在长上下文上天生吃亏,再怎么优化缓存都是带着镣铐跳舞。

第二梯队是KV Cache 量化。把缓存从 FP16 压到 FP8,直接省一半;压到 INT4 能省 75%。推理框架里一般叫kv_cache_dtype之类的参数。这个方案对模型质量会有轻微影响,但在长上下文场景中,很多工作表明 8bit KV Cache 的损失可以做到非常小,属于性价比很高的优化。

第三梯队是窗口化优化。有些框架支持 rolling KV Cache 或滑动窗口缓存,只保留最近的 N 个 token 的 KV,更早的直接丢。这对无限长文档的流式处理特别有用,但代价是模型会“忘记”早期内容,适合不需要完整访问历史的场景。

第四梯队是PagedAttention 式的显存管理。这是工程层面止痛药,它不减少 KV Cache 的总字节数,而是把显存切块按需分配,消除传统连续缓存造成的碎片化和浪费——你会发现有时候明明显存还够,但系统说 OOM,就是碎片太大导致的。vLLM 的核心卖点之一就在这里,Ollama 等也借助类似机制提升并发。

4.4 上线前的 KV Cache 巡检清单

结合我自己的部署经验,上线前我建议按这个顺序过一遍:

  • 用上面公式估算当前配置下 KV Cache 的大小,确认总显存有冗余;
  • 查一下框架日志里有没有 KV Cache 占用、显存利用率的输出,没有就手动打点;
  • 设一个略低于显存上限的上下文长度,而不是往框架允许的最大值上顶,留出爆缓冲;
  • 在目标并发数下做压测,观测显存曲线是否平稳,有没有随着对话轮次增长而失控;
  • 如果用的是前缀缓存,确认 prefix 命中的复用逻辑确实在生效(比如重复请求同样 prompt 时显存不涨)。

这套检查做完,基本能把部署环境里跟 KV Cache 相关的坑踩掉八成。

5. 常见问题排查与避坑实录

5.1 推理爆显存时先看什么

最典型的场景就是模型加载完一推理就 OOM。这时候先别急着降模型精度,第一步去查 KV Cache 的预算。做法很简单:用nvidia-smi或参考框架日志里 BW 占用曲线,看看显存是在几秒内瞬间拉满,还是随着生成长度逐步爬升。

如果是逐步爬升,基本就是 KV Cache 随生成长度增长导致,排查方向锁定上下文长度和并发。你可以临时把上下文长度减半,如果显存显著下降且问题消失,那就是缓存分配过大。如果上下文已经很短了还是 OOM,再考虑权重量化或者换小模型。

还有一个容易被忽略的坑:有些框架默认初始化 KV Cache 时会按最大支持上下文一次性申请显存,而不是按实际已经生成的 token 长度动态增长。这种情况下,哪怕你只生成 100 个 token,只要配置里写了 32K,它也会把 32K 对应的缓存量先占上。所以有时候明明生成很短,显存却已经是满配状态,这就是原因。要解决,就要把max_model_len或num_ctx严禁按实际需要设,别图省事填一个很大的数。

5.2 生成长度一长,速度明显变慢

这是一个非常普遍的体验:短文本生成飞快,长文本生成到后面越来越慢。不少人猜测这是模型“变笨”了,其实是计算量在涨——每一轮新 token 的注意力计算都要扫一遍全部 KV Cache,序列越长扫描越多。

这种情况的排查不复杂,但要注意区分两种“慢”:一种是预填充(处理 prompt)慢,那是 prompt 很长、初次计算 KV Cache 的代价;另一种是解码(逐 token 生成)慢,那是每一步注意力随 KV 增长线性变大的必然结果。前者可以用前缀缓存来规避,后者只能用更短的上下文、更小的模型或者更高效的注意力实现(如 FlashAttention 这类 IO 优化)来改善。

实测时你可以盯两个指标:Time To First Token(TTFT)和 Time Per Output Token(TPOT)。TTFT 过高的基本是前缀没命中或者 prompt 过长,TPOT 过高就要注意 KV Cache 的访存和计算开销是不是到瓶颈了。

5.3 长对话出现内容错乱、答非所问

这跟 KV Cache 的存储没什么直接关系,更多是长上下文里的注意力分配问题,也就是前面说的 lost in middle 等现象。排查思路分两步:

先确认是不是上下文长度超出了模型的有效范围。很多模型虽然支持长上下文,但有效注意力区间远没那么长。你可以把 16K 上下文的对话砍到 8K 再测一次,如果质量明显回升,说明模型本身在这个长度上已经力不从心了。

再确认有没有引入窗口化缓存。如果用了滑动窗口 KV Cache,模型对窗口外信息的依赖能力会被切断,早期对话内容会被新内容顶掉。这时候应该在产品层面引导用户把关键信息放在最近的几轮对话里,或者对关键内容做摘要折叠,而不是指望模型记住很长的历史。

5.4 几个写在最后的坑与心得

我在实际部署中踩过几个跟 KV Cache 相关的坑,挑三个最值得说的:

  • 多请求复用前缀缓存时,要注意系统提示词哪怕改一个空格或换行,哈希就会变,导致命中率急转直下。解决方法是把 system prompt 在存缓存前做规范化处理,文案层面保持严格一致。
  • 并发调大之后,如果不限制单路最大 token 数,某些用户的长文档请求会把整个显存吃光,影响同卡其他用户。线上环境一定要同时设并发数和单路上下文上限两道闸,缺一个都会出事。
  • 用了 KV Cache 量化后,如果量化粒度过粗,长上下文的连贯性会先于单句正确性出现劣化——先表现为跑题、前后矛盾,而不是明显的语法错误。发现这种情况,优先检查 KV Cache 量化位宽,而不是怀疑模型本身。

最后再分享一个小技巧:想快速知道你的模型在每个上下文长度下大概需要多少 KV Cache,不用翻文档,直接在推理框架里把上下文长度分别设成 1K、2K、4K、8K、16K,加载模型后观测显存差值,差值除以对应序列长度的变化量,就能算出单 token 的 KV Cache 成本。这个数字在你做容量规划时会非常好用——算清楚一条序列的增量成本,并发和长度的博弈就有了明确的尺子。

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

大模型训练中的并行策略:DP、TP与PP详解

1. 大模型训练为什么需要并行策略?当模型参数量突破十亿级别时,单张GPU的显存容量和计算能力就捉襟见肘了。以GPT-3为例,1750亿参数全精度存储就需要700GB显存,而当前最先进的H100 GPU显存仅为80GB。这就迫使我们必须将模型拆分到…

作者头像 李华
网站建设 2026/10/25 7:53:10

AI应用架构设计:从LLM、Agent到MCP的分层实践与避坑指南

1. 从一张架构图说起:为什么AI应用架构值得单独拎出来讲很多人第一次接触AI应用开发,脑子里想的都是"调个API就完事了"。我刚开始也是这么想的——拿个大模型接口,套个前端页面,能对话就算跑通了。直到真正接手一个要上…

作者头像 李华
网站建设 2026/10/25 7:52:47

Apache SeaTunnel 3月更新:实时计算与连接器生态全面升级

1. Apache SeaTunnel 3月动态全景扫描作为数据集成领域的明星项目,Apache SeaTunnel在2024年3月迎来了一系列重要更新。这个由Apache软件基金会孵化的开源项目,正在重塑企业级数据同步和ETL的工作范式。如果你正在评估下一代数据集成工具,或是…

作者头像 李华
网站建设 2026/10/25 7:44:53

Python深度学习人脸识别系统开发实战

1. 项目概述这个完整的人脸识别系统项目包包含了从设计到实现的全部核心要素:设计源文件、万字技术报告、系统讲解视频,以及配套的参考资料和图片素材。更难得的是,这个项目还支持根据实际需求进行定制化开发,底部提供的联系方式可…

作者头像 李华
网站建设 2026/10/25 7:41:10

YOLOPv2全景分割网络:多任务实时检测与优化实践

1. YOLOP/YOLOPv2项目概述YOLOP(You Only Look Once for Panoptic)是面向全景分割任务的高效一体化网络架构。作为YOLO系列在多任务学习领域的延伸,它首次实现了在单网络结构中同时完成目标检测、可行驶区域分割和车道线检测三大任务。2022年…

作者头像 李华
网站建设 2026/10/25 7:40:20

MySQL数据库安全加固:TDE加密与DBG网关实战

1. MySQL数据库安全加固的必要性最近在金融行业做数据安全合规项目时,客户审计报告明确要求对生产数据库实施字段级加密。这让我意识到许多企业对MySQL数据保护仍停留在基础认证层面。实际上,数据库作为核心数据载体,需要建立多层防护体系。以…

作者头像 李华