一、一句话定义

KV Cache,就是大模型推理时的"记忆便签"——把已经算过的注意力(Key和Value矩阵)存下来,下一个词就不用从头重算了。

你可能从来没有想过这个问题:当你问ChatGPT一个问题,它一口气吐出500个字的回答,第501个字怎么来的?

最直觉的答案:模型拿着你的问题+前500个字的回答,重新算一遍注意力,然后预测第501个字。

但这里有一个致命问题——每算一个新字,都要把前面所有字的注意力从头重算一遍。如果回答有1000个字,那就是:

  • 第1个字:算1次注意力
  • 第2个字:算2次注意力
  • 第3个字:算3次注意力
  • ……
  • 第1000个字:算1000次注意力

总共 1+2+3+...+1000 = 500,500次。这就是O(n²)的灾难。

而KV Cache做的事情简单到令人发笑:算了就存着,别丢了。第2个字算完后,把Key和Value矩阵存起来;第3个字只需要算自己这一份,然后跟存着的合并就行。总计算量从500,500次降到1000次,效率提升500倍

这就是KV Cache——没有它,大模型推理的速度会慢到让你怀疑是不是断网了。

二、从Transformer到KV Cache:一条必然的进化路径

要理解KV Cache,你必须先回忆Transformer的Self-Attention是怎么算的(我们在Transformer彻底讲透里讲过)。核心公式:

Attention(Q, K, V) = softmax(Q × K^T / √d) × V

每生成一个新token,模型需要:

  1. 用新token算出一个Query向量(Q)
  2. 所有已有token(包括新token本身)算出Key向量(K)和Value向量(V)
  3. 用Q去跟所有K做点积,得到注意力权重
  4. 用注意力权重加权所有V,得到输出

问题就在第2步——每次生成新token,都要把前面所有token的K和V重新算一遍。而这些K和V在上一步已经算过了!纯纯的重复劳动。

KV Cache的核心思想:算了就别丢,存着下次用

阶段 计算方式 KV Cache状态 速度
没有KV Cache 每个新token重算所有历史K/V 无缓存,每次从零开始 O(n²),越来越慢
有KV Cache 每个新token只算自己的K/V 历史K/V已缓存,直接拼接 O(n),几乎恒速

一个直觉类比:你考试做数学题,每做一道新题都需要用到前面所有题的中间结果。没有KV Cache = 每道题从头重算;有KV Cache = 把中间结果写在草稿纸上,直接翻看。

三、KV Cache的工作原理:Prefill与Decode两个阶段

大模型推理分两个截然不同的阶段:

阶段1:Prefill(预填充)

用户输入prompt,模型一次性并行处理所有prompt token。这一步是密集计算,因为需要:

  • 算出所有prompt token的Q、K、V矩阵
  • 做完整的Self-Attention计算
  • 把所有K和V存入KV Cache
  • 输出第一个新token

Prefill阶段是计算密集型(compute-bound),GPU算力是瓶颈。但因为所有prompt token可以并行处理,所以即使prompt很长,Prefill也相对快。

阶段2:Decode(逐字生成)

模型逐个token生成回答,每一步:

  • 只算当前新token的Q、K、V(计算量极小)
  • 把新的K、V追加到KV Cache
  • 用新Q跟整个KV Cache里的所有K做注意力计算
  • 输出下一个token

Decode阶段是内存密集型(memory-bound),瓶颈不是GPU算力,而是把KV Cache从显存搬到GPU的速度。这就是为什么长文本生成时,GPU利用率可能只有30-40%——算力闲置,但在等显存传输。

特征 Prefill阶段 Decode阶段
计算模式 并行处理所有prompt token 逐个token串行生成
瓶颈类型 计算密集(compute-bound) 内存密集(memory-bound)
KV Cache 首次创建,全部写入 逐步追加,只增不减
GPU利用率 高(~90%) 低(~30-40%)
速度体验 用户感知:模型"思考"了一下 用户感知:文字一个一个蹦出来

你可能注意到了:你在ChatGPT里输入一个长问题后,会有几秒钟的"思考"停顿(Prefill),然后文字开始流畅地涌出(Decode)。这两个阶段的体感差异,正是Prefill vs Decode的本质差异。

四、实战代码:用NumPy从零实现最小KV Cache

光说原理不够,我们用代码实现一个最小可运行的KV Cache,让你亲手看到它怎么工作:

import numpy as np

class MiniKVCacheAttention:
    """最小KV Cache自注意力实现——理解原理就够了"""

    def __init__(self, d_model=64, n_heads=4):
        self.d_model = d_model
        self.n_heads = n_heads
        self.d_k = d_model // n_heads  # 每个头的维度

        # 随机初始化投影矩阵(真实模型是训练出来的)
        self.W_q = np.random.randn(d_model, d_model) * 0.1
        self.W_k = np.random.randn(d_model, d_model) * 0.1
        self.W_v = np.random.randn(d_model, d_model) * 0.1

        # KV Cache:存储历史token的Key和Value
        self.k_cache = None  # shape: (n_heads, cached_len, d_k)
        self.v_cache = None  # shape: (n_heads, cached_len, d_k)

    def _split_heads(self, x):
        """把 (seq_len, d_model) 拆成 (n_heads, seq_len, d_k)"""
        return x.reshape(x.shape[0], self.n_heads, self.d_k).transpose(1, 0, 2)

    def _merge_heads(self, x):
        """把 (n_heads, seq_len, d_k) 合回 (seq_len, d_model)"""
        return x.transpose(1, 0, 2).reshape(x.shape[1], self.d_model)

    def forward_new_token(self, new_token_embedding, use_cache=True):
        """
        处理一个新token,返回输出向量
        use_cache=True:使用KV Cache(只算新token的K/V)
        use_cache=False:不用缓存(重算所有历史token的K/V)
        """
        # 1. 投影:算出新token的Q、K、V
        q_new = new_token_embedding @ self.W_q  # (1, d_model)
        k_new = new_token_embedding @ self.W_k  # (1, d_model)
        v_new = new_token_embedding @ self.W_v  # (1, d_model)

        # 2. 拆分多头
        q_h = self._split_heads(q_new)  # (n_heads, 1, d_k)
        k_h = self._split_heads(k_new)  # (n_heads, 1, d_k)
        v_h = self._split_heads(v_new)  # (n_heads, 1, d_k)

        if use_cache and self.k_cache is not None:
            # 3a. 有缓存:把新K/V追加到缓存,然后直接用
            self.k_cache = np.concatenate([self.k_cache, k_h], axis=1)
            self.v_cache = np.concatenate([self.v_cache, v_h], axis=1)
            all_k = self.k_cache
            all_v = self.v_cache
        else:
            # 3b. 无缓存:需要外部提供所有历史token重新计算
            # (这里简化为只有当前token,实际要重算所有)
            if use_cache:
                # 首次调用,创建缓存
                self.k_cache = k_h
                self.v_cache = v_h
            all_k = k_h
            all_v = v_h

        # 4. 注意力计算:Q × K^T → softmax → × V
        scores = q_h @ all_k.transpose(0, 2, 1) / np.sqrt(self.d_k)
        weights = np.exp(scores - np.max(scores, axis=-1, keepdims=True))
        weights = weights / np.sum(weights, axis=-1, keepdims=True)
        output = weights @ all_v  # (n_heads, 1, d_k)

        # 5. 合并多头
        return self._merge_heads(output)  # (1, d_model)

    def prefill(self, prompt_embeddings):
        """Prefill阶段:一次性处理所有prompt token"""
        # 一次性算所有prompt token的Q/K/V
        all_q = prompt_embeddings @ self.W_q
        all_k = prompt_embeddings @ self.W_k
        all_v = prompt_embeddings @ self.W_v

        # 拆分多头
        q_h = self._split_heads(all_q)
        k_h = self._split_heads(all_k)
        v_h = self._split_heads(all_v)

        # 初始化KV Cache(存下所有prompt的K/V)
        self.k_cache = k_h  # (n_heads, prompt_len, d_k)
        self.v_cache = v_h  # (n_heads, prompt_len, d_k)

        # 完整注意力计算(所有token互相看)
        scores = q_h @ k_h.transpose(0, 2, 1) / np.sqrt(self.d_k)
        weights = np.exp(scores - np.max(scores, axis=-1, keepdims=True))
        weights = weights / np.sum(weights, axis=-1, keepdims=True)
        output = weights @ all_v

        return self._merge_heads(output)


# ===== 实战演示 =====
d_model = 64
cache_attn = MiniKVCacheAttention(d_model=d_model, n_heads=4)

# 模拟prompt:5个token的embedding
prompt = np.random.randn(5, d_model)
print("=== Prefill阶段 ===")
prefill_output = cache_attn.prefill(prompt)
print(f"处理了5个prompt token,KV Cache已存5组K/V")
print(f"KV Cache shape: K={cache_attn.k_cache.shape}, V={cache_attn.v_cache.shape}")

# 模拟Decode:逐个生成新token
print("\n=== Decode阶段(使用KV Cache)===")
for step in range(1, 4):
    new_token = np.random.randn(1, d_model)
    output = cache_attn.forward_new_token(new_token, use_cache=True)
    cached_len = cache_attn.k_cache.shape[1]
    print(f"生成第{step}个token → KV Cache现在有{cached_len}组K/V "
          f"(只算1份新的,复用了{cached_len-1}份旧的)")

运行这段代码,你会看到KV Cache从5组增长到8组——每一步只多算1份K/V,而不是从头重算5、6、7份。这就是KV Cache的核心价值:增量计算,而非全量重算。

五、一行公式算清楚:KV Cache到底吃多少显存?

KV Cache不是免费的午餐——它用GPU显存计算时间。具体吃多少?一行公式:

KV Cache显存 = 2 × n_layers × seq_len × d_model × dtype_bytes × n_batch

其中:

  • 2:Key和Value各一份
  • n_layers:Transformer层数(GPT-4约96层,Llama-3-70B约80层)
  • seq_len:上下文长度(含prompt+已生成的回答)
  • d_model:模型维度(通常4096-8192)
  • dtype_bytes:数据类型大小(FP16=2字节,FP32=4字节,INT8=1字节)
  • n_batch:并发请求数

来算几个真实场景:

模型 层数 d_model 上下文长度 精度 单请求KV Cache
Llama-3-8B 32 4096 8K FP16 ~1GB
Llama-3-70B 80 8192 8K FP16 ~10GB
GPT-4级模型 ~96 ~12288 128K FP16 ~60GB+

看到问题了吗?GPT-4级模型处理128K上下文,KV Cache单请求就要60GB显存——而一张A100才80GB,还要留空间给模型权重本身(约70GB)。所以128K上下文在单卡上根本跑不起来。

这就引出了KV Cache优化的四大武器。

六、KV Cache优化四件套:从暴力缓存到精细管理

1. GQA(Grouped Query Attention)——少存几份Key

标准Multi-Head Attention:每个Query头都有自己专属的Key头和Value头。如果有64个Query头,就要存64份K和64份V。

GQA的做法:让几个Query头共享同一组Key/Value头。比如8个Query头共享1组K/V,这样只需存8份K和8份V,比原来的64份缩减8倍

Llama-3-8B就用了GQA(32个Q头共享8组K/V),KV Cache缩减4倍。代价:少量精度损失,但实际评测几乎无影响。

2. MQA(Multi-Query Attention)——只存一份Key

GQA的极端版本:所有Query头共享唯一一组K/V。64个Q头,只存1份K和1份V,缩减64倍。

最早由Google在PaLM中使用。优点是KV Cache极小,缺点是模型质量下降更明显,现在大多数模型选择GQA这种"中间路线"。

方案 Q头数 K/V头数 KV Cache缩减 精度影响
MHA(标准) 64 64 1x(基准)
GQA(分组共享) 64 8 8x缩减 极小
MQA(全共享) 64 1 64x缩减 有损

3. PagedAttention——像操作系统管内存一样管KV Cache

vLLM团队(UC Berkeley)2023年的杀手级创新。灵感来自操作系统的虚拟内存分页机制

  • 传统KV Cache:给每个请求预分配一块连续显存(按最大上下文长度分配)。问题是:请求实际可能只用了2K token,但你预分配了128K的空间——浪费96%
  • PagedAttention:把KV Cache切成固定大小的"页"(比如每页存256个token的K/V),按需分配,用多少给多少。就像操作系统不会在程序启动时就分配4GB内存,而是按需给页

效果:显存利用率从20-40%提升到90%+,同等硬件吞吐量提升2-4倍。这是vLLM能成为2024年最火推理框架的核心原因。

4. KV Cache量化(Quantization)——把缓存从FP16压到INT8/FP8

FP16每个数值占2字节,INT8只占1字节——直接减半KV Cache显存占用。KV Cache的精度要求比模型权重更低(它只是注意力权重,不是最终输出),所以INT8量化几乎不影响模型质量。

更激进的做法:FP8量化(1字节,NVIDIA H100原生支持),或INT4量化(0.5字节,KIVI论文提出的方法)。KV Cache从FP16压到INT4,显存缩减4倍

七、KV Cache在AI Coding中的实战意义

你可能会想:"KV Cache是推理引擎内部的事,跟我写代码有什么关系?"关系非常大——它直接影响你用AI Coding工具时的速度、成本和上下文窗口

场景1:为什么Cursor能秒级补全,而本地跑Llama却要等好几秒?

Cursor背后的推理服务用了GQA + PagedAttention + FP8量化三件套,KV Cache占用极小,Decode阶段GPU利用率拉满。你本地跑Llama-3-8B?没有PagedAttention,没有批处理优化,KV Cache把显存吃光,GPU算力闲置——慢是必然的。

场景2:为什么Claude Code能处理整个代码仓库,而GPT-4在长文件里会"忘事"?

我们在Context Engineering彻底讲透里讲过"Lost in the Middle"效应——长上下文中间的信息容易被忽略。但还有一个更底层的原因:KV Cache显存不够,推理引擎被迫截断上下文。128K上下文需要的KV Cache太大,很多服务实际只跑了32K-64K,然后对外宣称"支持128K"——其实是用滑动窗口+摘要拼接来模拟的。

场景3:为什么并发用户多时,AI Coding工具会变慢?

每个并发请求都有自己独立的KV Cache。10个并发请求,KV Cache显存需求乘10。显存不够时,推理引擎要么排队等待,要么把部分请求的KV Cache卸载到CPU内存(速度暴跌100倍)。这就是为什么高峰期AI服务变慢——瓶颈不是算力,是显存被KV Cache占满了

场景4:你自己部署推理服务,如何优化KV Cache?

# 用vLLM启动推理服务——自带PagedAttention + GQA优化
python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Meta-Llama-3-8B-Instruct \
    --kv-cache-dtype fp8 \          # KV Cache用FP8量化,显存减半
    --max-model-len 8192 \          # 最大上下文长度
    --gpu-memory-utilization 0.9 \  # GPU显存利用率90%
    --enable-chunked-prefill True   # 分块Prefill,避免长prompt阻塞短请求

# 性能对比(单张A100-80GB,Llama-3-8B):
# - 无优化(HuggingFace默认):吞吐 ~50 tokens/s
# - vLLM + PagedAttention:吞吐 ~800 tokens/s(16倍提升)
# - vLLM + FP8量化:吞吐 ~1200 tokens/s(24倍提升)

八、5个常见误区:KV Cache不是你以为的那样

误区1:"KV Cache就是缓存,跟Redis一样"

错。Redis缓存的是最终结果(整个回答),KV Cache缓存的是中间计算结果(注意力矩阵的K和V分量)。Redis缓存让第二次请求更快,KV Cache让同一次请求中的后续token更快。两者解决的问题完全不同。

误区2:"上下文窗口越大越好,128K肯定比8K强"

不一定。128K上下文意味着KV Cache需要60GB+显存,推理服务要么用更多GPU(成本翻倍),要么用KV Cache压缩(精度受损),要么用滑动窗口模拟(中间信息丢失)。在很多实际场景中,8K上下文 + 好的RAG比128K裸跑效果好得多——我们在RAG彻底讲透里讲过这个道理。

误区3:"KV Cache会自动管理,我不需要操心"

在消费ChatGPT时确实不需要操心——OpenAI替你管理了。但当你自己部署推理服务设计AI Coding工作流时,KV Cache就是最核心的工程考量之一。上下文长度设置多大?用FP16还是FP8?批处理多少并发?这些决策都围绕KV Cache展开。

误区4:"KV Cache让推理变快,所以越大越好"

KV Cache让Decode阶段变快(避免重复计算),但也让Decode阶段变慢(更多K/V要做注意力计算,更多显存要搬运)。这是一个权衡:缓存越多,计算越省,但显存越紧张。超长上下文时,注意力计算的O(n)复杂度本身也变慢——KV Cache消除了O(n²),但O(n)依然随长度增长。

误区5:"有了KV Cache,就不需要RAG了"

恰恰相反。KV Cache和RAG解决的是不同层面的问题

  • KV Cache解决:同一个请求中,怎么高效地重复利用已经算过的注意力
  • RAG解决:不同请求之间,怎么把外部知识高效地注入上下文

两者是互补关系,不是替代关系。RAG让上下文更精炼(减少KV Cache负担),KV Cache让RAG注入的内容在Decode时被高效复用。好的AI Coding系统 = RAG精选内容 + KV Cache高效推理

九、未来趋势:KV Cache正在从"幕后配角"变成"前台主角"

趋势1:KV Cache共享——多个请求复用同一份缓存

如果你10个用户都在问同一个代码仓库的上下文,没必要让每个请求都独立存一份KV Cache。Prefix Caching(前缀共享缓存)让所有请求共享prompt部分的KV Cache,只在回答部分各自独立。vLLM已经支持了这个特性,效果:并发场景吞吐量再提升2-3倍

趋势2:KV Cache卸载——GPU不够用时搬到CPU/SSD

当显存紧张时,把不活跃的KV Cache搬到CPU内存甚至NVMe SSD,需要时再搬回来。这就像操作系统的Swap机制。代价是速度下降(CPU内存比GPU显存慢50-100倍,SSD更慢),但换来的是理论上无限上下文长度。Mooncake、LoRA Serve等系统都在做这个方向。

趋势3:动态KV Cache淘汰——不是所有token的缓存都值得保留

注意力权重分布是不均匀的——大部分token只被极少数其他token"注意到"。Scissorhands、H2O等算法提出:淘汰注意力权重最低的token的KV Cache,只保留"重要"token的缓存。效果:KV Cache缩减50-70%,模型质量几乎不变。这跟我们在Context Engineering里讲的"上下文优先级排序"是同一思路——不是所有信息都值得保留。

趋势4:跨模型KV Cache复用——微调模型共享基础模型的缓存

LoRA微调只改了很小一部分权重,大部分K/V投影矩阵没变。这意味着基础模型的KV Cache可以直接给微调模型用,只需在LoRA部分做少量修正。这个想法已经在Lambda Labs的推理服务中落地,让LoRA模型切换几乎零延迟

十、总结:KV Cache——大模型推理效率的隐藏引擎

KV Cache不是一个"高级优化技巧",而是大模型推理的基础设施——没有它,Transformer的Self-Attention在Decode阶段就是O(n²)的灾难,生成1000个token要重算50万次注意力。

理解KV Cache,你就能理解:

  • 为什么ChatGPT回答第一个字时有停顿(Prefill),后面就流畅了(Decode + KV Cache)
  • 为什么长上下文推理需要这么多GPU显存(KV Cache是显存大户)
  • 为什么并发请求多时AI服务会变慢(KV Cache显存不够了)
  • 为什么GQA/MQA/PagedAttention这些技术如此重要(它们都是KV Cache优化)
  • 为什么RAG和KV Cache是互补而非替代(RAG精炼内容,KV Cache高效推理)

对于AI Coding开发者来说,KV Cache是你理解和优化AI推理性能的第一道门槛。不懂KV Cache,你永远只会觉得"模型就是慢"或"显存就是不够",而不明白背后的真实瓶颈在哪里。

记住这句话:KV Cache不是缓存策略,是推理引擎的心脏。你不需要亲手写KV Cache代码,但你需要知道它为什么存在、怎么工作、哪里可能出问题——因为这些直接决定了你用AI Coding工具时的速度、成本和上下文上限。

附录:彻底讲透系列文章导航

本系列已发布20+篇AI Coding基础概念深度解读文章,以下是完整导航:

# 主题 文章链接
1 LLM 大语言模型 LLM彻底讲透:从"下一个词预测"到通用人工智能
2 Transformer架构 Transformer彻底讲透:为什么所有大模型都是同一套架构
3 Token 分词 Token彻底讲透:大模型"读"不了一个字,但能理解整个世界
4 Embedding 向量嵌入 Embedding彻底讲透:大模型为什么能把猫和狗理解成近亲
5 Prompt Engineering 提示工程 Prompt Engineering彻底讲透:从"说人话"到"驱动AI"
6 Function Calling 函数调用 Function Calling彻底讲透:大模型"动手"调用外部世界
7 Agent 智能体 Agent彻底讲透:让AI从问答工具进化成能自主干活的智能体
8 MCP 协议 MCP彻底讲透:AI Coding的万能接口
9 RAG 检索增强生成 RAG彻底讲透:让大模型从胡说八道到言之有据
10 Skill 技能 Skill彻底讲透:技能如何成为大模型能力的真正放大器
11 Vibe Coding 氛围编程 Vibe Coding彻底讲透:AI Coding的终极形态
12 Fine-tuning 微调 Fine-tuning彻底讲透:AI开发者必须跨越的分水岭
13 Temperature 温度调节 Temperature彻底讲透:一个参数如何决定大模型是"严谨工程师"还是"脑洞诗人"
14 多模态大模型 多模态大模型彻底讲透:从看懂图片到生成视频
15 AI 护栏 Guardrails AI护栏彻底讲透:如何用"防呆机制"让AI真正可用
16 Reasoning 推理模型 Reasoning模型彻底讲透:为什么o1/R1让大模型学会"先想再答"
17 Context Engineering 上下文工程 Context Engineering彻底讲透:管理上下文比写提示词更重要
18 大模型幻觉 Hallucination 大模型幻觉彻底讲透:为什么AI总是一本正经地胡说八道
19 KV Cache 缓存加速 🔥 本文

系列还在继续,下一期我们可能会聊聊向量数据库MoE混合专家量化QuantizationRLHF对齐CoT思维链——每一个都是AI Coding开发者需要理解的底层概念。关注我,别迷路。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐