游乐游手机版
首页/AI教程/文章详情

LLM缓存机制深度解析:从矩阵运算到Prompt Cache

时间:2026-08-03 22:10
问题背景 大语言模型(LLM)在执行推理任务时,需完成海量矩阵运算,其中 Self-Attention 机制成为最核心的计算瓶颈。每当生成一个新 token,模型都必须重新计算所有历史 token 的 Key(K)与 Value(V)向量,这种重复性计算造成了严重的算力浪费。若要深入理解 LLM 推

问题背景

大语言模型(LLM)在执行推理任务时,需完成海量矩阵运算,其中 Self-Attention 机制成为最核心的计算瓶颈。每当生成一个新 token,模型都必须重新计算所有历史 token 的 Key(K)与 Value(V)向量,这种重复性计算造成了严重的算力浪费。若要深入理解 LLM 推理延迟、显存消耗及定价策略,就必须系统掌握 Token 化原理、推理阶段划分、Attention 计算方式、KV Cache 以及 Prompt Cache 缓存机制。

核心概念

Token 与 Tokenizer

LLM 无法直接理解原始文本,必须借助 Tokenizer 将文本转化为模型可识别的数字序列。每个数字对应一个 Token,而 Token 是模型所能处理的最小语义单元。目前主流方案为 BPE(Byte Pair Encoding,字节对编码),其核心流程是:从单个字符出发,反复合并出现频率最高的相邻字符对,逐步构建出一个固定大小的词表(例如 GPT-4 的词表规模约为 100,000 个 Token)。

示例:输入文本“我喜欢猫”,Tokenizer 处理后可能得到:

"我" → Token ID: 101
"喜欢" → Token ID: 202
"猫" → Token ID: 303
最终: [101, 202, 303]

规律:

  • 高频词通常整体编码为 1 个 Token(如英文 theHello)。
  • 罕见词则会被拆分为多个子词(例如 LangfuseLang + f + use,共占用 3 个 Token)。
  • 中文场景下,每个汉字通常对应 1 到 2 个 Token。

LLM 推理的两个阶段

一次完整的 LLM 调用过程可划分为 Prefill 阶段与 Decode 阶段。

  • Prefill 阶段:该阶段并行处理全部输入 Token,一次性计算出所有 Token 的 Q、K、V,并生成 KV Cache。此阶段吞吐量高,具备良好的并行计算能力。
  • Decode 阶段:该阶段逐个生成输出 Token,每一步仅处理当前新 Token 的 Q、K、V,并复用之前缓存的 KV 数据,采用串行方式执行,每次仅生成 1 个 Token。

工作原理

Self-Attention 机制

Attention 要解决的核心问题是:“在生成下一个词时,模型应该重点关注前面哪些词?” 以句子“我喜欢猫,它们很___”为例,当模型生成“___”时,需要优先关注“猫”和“它们”,而非“我”或“喜欢”。

每个 Token 会被转换为三个向量:

向量含义类比
Q(Query)“我在寻找什么信息”提问者
K(Key)“我能提供什么信息”标签或索引
V(Value)“我的实际内容”答案内容

计算公式:

Attention(Q, K, V) = softmax(Q × K^T / √d) × V
  • Q × K^T:衡量每个 Token 与所有其他 Token 之间的相关度得分。
  • / √d:缩放操作,防止数值过大导致梯度问题。
  • softmax:将得分归一化为概率分布(所有权重之和为 1)。
  • × V:利用权重对 Value 进行加权求和,得到最终的输出表示。

具体 Demo:极简模型计算过程

假设一个极简模型:仅包含 1 层、1 个注意力头,维度 d=4。输入 3 个 Token:["我", "喜欢", "猫"]

Step 1:Embedding。每个 Token ID 通过查表获得 4 维向量:

"我" → e₁ = [1.0, 0.0, 0.5, 0.3]
"喜欢" → e₂ = [0.2, 1.0, 0.1, 0.8]
"猫" → e₃ = [0.6, 0.3, 1.0, 0.1]

Step 2:通过权重矩阵计算 Q、K、V。模型包含三个可学习的权重矩阵 W_Q、W_K、W_V(训练阶段学习得到,推理阶段固定不变)。假设 W_K 为 4×4 矩阵:

假设 W_K(4×4 矩阵):
┌                   ┐
│ 0.1  0.2  0.0  0.3 │
│ 0.4  0.1  0.2  0.0 │
│ 0.0  0.3  0.1  0.2 │
│ 0.2  0.0  0.4  0.1 │
└                   ┘
K₁ = e₁ × W_K = [1.0, 0.0, 0.5, 0.3] × W_K = [0.16, 0.35, 0.17, 0.43]

同理可算出所有 K 和 V:

K₁ = [0.16, 0.35, 0.17, 0.43] ← "我" 的 Key
K₂ = [0.30, 0.15, 0.38, 0.14] ← "喜欢" 的 Key
K₃ = [0.12, 0.45, 0.13, 0.38] ← "猫" 的 Key
V₁ = [0.50, 0.20, 0.10, 0.40] ← "我" 的 Value
V₂ = [0.30, 0.60, 0.40, 0.10] ← "喜欢" 的 Value
V₃ = [0.70, 0.10, 0.80, 0.30] ← "猫" 的 Value

这些 K 和 V 正是需要缓存的关键数据,因为在后续生成新 Token 的过程中,它们会被反复调用。

Step 3:计算 Attention(以最后一个 Token “猫”为例)

Q₃ = e₃ × W_Q = [0.25, 0.40, 0.18, 0.55]("猫" 的 Query)
# "猫" 对每个 Token 的关注度 = Q₃ · Kᵢ(点积)
score₁ = Q₃ · K₁ = 0.25×0.16 + 0.40×0.35 + 0.18×0.17 + 0.55×0.43 = 0.45
score₂ = Q₃ · K₂ = 0.25×0.30 + 0.40×0.15 + 0.18×0.38 + 0.55×0.14 = 0.28
score₃ = Q₃ · K₃ = 0.25×0.12 + 0.40×0.45 + 0.18×0.13 + 0.55×0.38 = 0.44
# 缩放 + softmax
scaled = [0.45/√4, 0.28/√4, 0.44/√4] = [0.225, 0.14, 0.22]
weights = softmax([0.225, 0.14, 0.22]) ≈ [0.36, 0.29, 0.35]
# 加权求和 Value
output₃ = 0.36×V₁ + 0.29×V₂ + 0.35×V₃ = [0.51, 0.28, 0.43, 0.28]

该 output₃ 会继续传递至 FFN(前馈神经网络)并最终经过 softmax,输出下一个 Token 的概率分布。

执行流程

KV Cache:单次请求内的缓存

问题:无 KV Cache 时的重复计算。在 Decode 阶段,每生成一个 Token 都需要对所有历史 Token 执行 Attention 操作。若不引入缓存机制,生成第 N 个 Token 时,必须重新计算从输入到第 N-1 个所有 Token 的 K、V,这会导致大量重复计算,严重浪费算力。

解决方案:存储 K 和 V。Prefill 阶段并行计算出所有输入 Token 的 K、V,并将其存入 KV Cache。在 Decode 阶段,每一步仅需计算新 Token 的 Q、K、V,然后使用 Q 与缓存中所有 K 进行点积运算,得到 Attention 输出,同时将新 Token 的 K、V 追加到缓存中。

逐步生成过程示例

Prefill 完成后 KV Cache 状态:

┌─ KV Cache ────────────────────────────────────────┐
│ 位置 0 ("我"): K₁=[0.16,0.35,0.17,0.43] V₁=[0.50,0.20,0.10,0.40] │
│ 位置 1 ("喜欢"): K₂=[0.30,0.15,0.38,0.14] V₂=[0.30,0.60,0.40,0.10] │
│ 位置 2 ("猫"): K₃=[0.12,0.45,0.13,0.38] V₃=[0.70,0.10,0.80,0.30] │
└───────────────────────────────────────────────────┘

生成第 1 个 output Token(假设为“,”):

"," 的 embedding → 算 Q_new、K_new、V_new(1 次矩阵乘法)
Q_new 和 Cache 中的 [K₁, K₂, K₃] 做 Attention → 得到 output 向量 → FFN → softmax → 采样 → ","
Cache 追加 K₄、V₄:
┌─ KV Cache ────────────────────────────────────────┐
│ 位置 0 ("我"): K₁V₁                                 │
│ 位置 1 ("喜欢"): K₂V₂                               │
│ 位置 2 ("猫"): K₃V₃                                 │
│ 位置 3 (","): K₄V₄  ← 新增                        │
└───────────────────────────────────────────────────┘

生成第 2 个 output Token(假设为“它们”):

"它们" 的 embedding → 算 Q_new、K_new、V_new
Q_new 和 Cache 中的 [K₁, K₂, K₃, K₄] 做 Attention → 得到 output → "它们"
Cache 再追加:
┌─ KV Cache ────────────────────────────────────────┐
│ 位置 0 ("我"): K₁V₁                                 │
│ 位置 1 ("喜欢"): K₂V₂                               │
│ 位置 2 ("猫"): K₃V₃                                 │
│ 位置 3 (","): K₄V₄                                 │
│ 位置 4 ("它们"): K₅V₅  ← 新增                      │
└───────────────────────────────────────────────────┘

KV Cache 的大小

实际生产环境中,KV Cache 的显存占用非常可观:

KV Cache 大小 = 2(K和V) × 层数 × 注意力头数 × 头维度 × 序列长度 × 精度
以 Llama 3 70B 为例:
= 2 × 80层 × 8头(GQA) × 128维 × 8192长度 × 2字节(FP16) ≈ 2.6 GB(单个请求)

这也解释了为何 GPU 显存通常比算力更早成为瓶颈。

计算量对比

步骤无 KV Cache有 KV Cache节省比例
Decode 第 1 步计算 3 个 Token 的 K、V只计算 1 个节省 67%
Decode 第 10 步计算 12 个 Token 的 K、V只计算 1 个节省 92%
Decode 第 100 步计算 102 个 Token 的 K、V只计算 1 个节省 99%

参数作用

KV Cache 的 Key 由 Token 的位置索引决定,它缓存的是每一层、每个注意力头的 K 和 V 矩阵。而 Prompt Cache 的 Key 则由 Token 序列的哈希值决定,用于实现跨请求的复用。

示例说明

Prompt Cache:跨请求的缓存

问题:多轮对话中的重复计算。KV Cache 仅在单次请求生命周期内有效,请求结束后即被释放。在多轮对话场景中,每一轮都需要重新发送完整的上下文,导致 Prefill 阶段反复计算相同前缀的 KV,造成大量冗余计算。

解决方案:跨请求复用 KV Cache。服务端将 Prefill 阶段计算出的 KV Cache 持久化存储,当后续请求的前缀相同时,可直接复用已有缓存。具体实现时,通过分块哈希机制支持最长前缀匹配:

假设 block_size = 4(实际场景中通常为 64 或 128)
Token 序列: [101, 202, 303, 404, 505, 601, 602, 603]
Block 0: hash([101, 202, 303, 404]) = "a1b2c3"
Block 1: hash([505, 601, 602, 603]) = "d4e5f6"
缓存结构(类似 Radix Tree):
┌──────────────────────────────────┐
│ "a1b2c3" → KV Cache (位置 0~3)   │
│ "a1b2c3:d4e5f6" → KV Cache (0~7) │
└──────────────────────────────────┘

新请求到达时,逐块计算哈希并进行匹配:

新请求 Token: [101, 202, 303, 404, 505, 601, 999, 888]
Block 0: hash([101, 202, 303, 404]) = "a1b2c3" → ✅ 命中
Block 1: hash([505, 601, 999, 888]) = "x7y8z9" → ❌ 不匹配
结果: 复用 Block 0 的 KV Cache(前 4 个 Token),Block 1 需要重新计算。

缓存命中的条件

✅ 命中条件:
- 严格前缀匹配(从第一个 Token 开始,必须连续一致)
- 达到最小缓存粒度(Anthropic 要求 ≥ 1024 tokens,OpenAI 要求 ≥ 128 tokens)
❌ 不命中的情况:
- 中间任何一个 Token 不同 → 从该位置开始,后续全部失效
- 相同的文本但 Token 化方式不同(几乎不会发生)
- 缓存过期(TTL 通常设置为 5 到 10 分钟)

多轮对话完整 Demo

═══ 第 1 轮 ════════════════════════════════════════════════
发送: [System: "你是一个AI助手,请用中文回答问题。"] ← 50 tokens
       [User: "什么是机器学习?"] ← 10 tokens
处理: 前缀匹配: 无缓存,全部 60 tokens 做 Prefill
缓存写入: hash(前60 tokens) → 存入缓存池
计费: Input: 60 tokens × 全价; Output: 200 tokens × 全价

═══ 第 2 轮 ════════════════════════════════════════════════
发送: [System: 同上] ← 50 tokens (同上)
       [User: "什么是机器学习?"] ← 10 tokens (同上)
       [Assistant: "机器学习是...(200 tokens)"] ← 200 tokens (新增)
       [User: "能举个例子吗?"] ← 8 tokens (新增)
处理: 前缀匹配: 前60 tokens 命中缓存 ✅
       → 加载 KV Cache(几乎零 GPU 计算)
       → 只对后 208 tokens 做 Prefill
计费: Input (cached): 60 tokens × 0.1 倍价格
       Input (uncached): 208 tokens × 全价
       Output: 150 tokens × 全价

═══ 第 3 轮 ════════════════════════════════════════════════
发送: [前面所有内容,共468 tokens] ← 大部分和第2轮重复
       [User: "还有什么应用?"] ← 8 tokens
处理: 前缀匹配: 前268 tokens 命中缓存 ✅(第2轮时已缓存)
       → 只对后 208 tokens 做 Prefill
计费: Input (cached): 268 tokens × 0.1 倍
       Input (uncached): 208 tokens × 全价
       Output: 180 tokens × 全价

费用模型

推理费用 = GPU 计算时间 × GPU 单价。三种 Token 的 GPU 消耗对比如下:

Token 类型GPU 执行内容算力消耗定价策略
Input (uncached)完整 Prefill:计算 Q、K、V,执行 Attention中等(可并行,效率较高)基准价
Input (cached)从显存或内存中加载 KV Cache极低(主要为数据搬运)基准价 × 0.1~0.5
Output完整 Decode:每个 token 执行一遍全模型高(串行执行,GPU 利用率低)基准价 × 3~5

以 Claude Sonnet 4 为例:

Input: $3 / 1M tokens
Input (cached): $0.3 / 1M tokens ← 1 折
Output: $15 / 1M tokens ← 5 倍于 input

Output 价格更高的原因在于:Decode 阶段采用串行方式,GPU 利用率较低,且内存带宽瓶颈导致大部分时间耗费在数据搬运上。

优势与限制

  • KV Cache 优势:大幅减少 Decode 阶段的重复计算,可节省 90% 以上的算力,有效降低推理延迟。
  • KV Cache 限制:仅在同一请求内有效,请求结束后即被丢弃;显存占用随序列长度线性增长,长序列场景下容易成为瓶颈。
  • Prompt Cache 优势:支持跨请求复用,显著减少 Prefill 计算量,降低推理成本与延迟;特别适用于多轮对话和固定 System Prompt 的场景。
  • Prompt Cache 限制:需要严格的前缀匹配,动态内容可能导致缓存失效;缓存命中需满足最小粒度要求,且存在 TTL 过期限制。

常见误区

  • 认为 Output Token 本身复杂度更高导致价格更贵。实际上,Output Token 价格高是因为 Decode 阶段串行执行且 GPU 利用率偏低,而非 Token 本身更复杂。
  • 认为 Prompt Cache 缓存的是整个 Prompt 的文本。实际上,缓存的是 Token 序列对应的 K、V 矩阵,而非文本本身。
  • 认为缓存命中条件较为宽松。实际要求严格的前缀匹配,动态内容(如时间戳)会破坏缓存,导致完全失效。

适用场景

  • KV Cache 适用于所有 LLM 推理场景,属于标准实现。
  • Prompt Cache 特别适用于:多轮对话应用、带有固定 System Prompt 的服务、Agent 中 Tool 定义固定的场景。在这些场景下,通过合理设计消息结构(将静态内容前置、动态内容后置)可最大化缓存命中率。

内容总结

LLM 推理中的缓存机制可划分为两个层级:

缓存类型缓存内容Key 类型作用范围节省的计算量
KV Cache每层的 K、V 矩阵Token 的位置索引单次请求内Decode 阶段避免重算历史 Token
Prompt Cache整段前缀的 KV CacheToken 序列的哈希值跨请求Prefill 阶段避免重算相同前缀

深入理解这两层缓存机制,即可清晰把握 LLM 推理延迟、显存占用以及定价逻辑的根本成因。

来源:https://juejin.cn/post/7632162180318052393
上一篇码道AI编程助手教你开发记忆翻牌网页小游戏 下一篇全球十大AI智能体技术生态排名与深度分析
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
WorkBuddy使用一个月避坑指南:5个常见问题及解决方案
AI教程 · 2026-08-05

WorkBuddy使用一个月避坑指南:5个常见问题及解决方案

使用WorkBuddy一个月,踩过指令模糊、未指定输出格式、反复打断任务、积分过期、未验证结果五个坑。对应解法:明确文件路径、动作、维度、格式和文件名;指定输出格式;耐心等待;优先使用快过期积分;抽查验证汇总逻辑。

图片生成任务到用户隔离:AIGC后端与PostgreSQL建模实践
AI教程 · 2026-08-05

图片生成任务到用户隔离:AIGC后端与PostgreSQL建模实践

基于AIGCCreativeStudio实践,后端采用Express+TypeScript与PostgreSQL17,通过users、generation_tasks、images三表模型实现任务状态机、图片本地存储及受认证访问,确保用户隔离与资源安全。

动态代码拖累SEO?用Gofair纯静态页面剔除冗余代码
AI教程 · 2026-08-05

动态代码拖累SEO?用Gofair纯静态页面剔除冗余代码

静态页面加载速度快,搜索引擎爬取效率高,优于动态建站。某孕产妇用品企业改用Gofair静态建站,五天多关键词冲至谷歌首页。SEO效果需通过关键词反查验证,流量数据易被干扰。未来静态页面策略将更主流。

WorkBuddy AI工作台实操教程 零基础搞定周报与数据分析
AI教程 · 2026-08-05

WorkBuddy AI工作台实操教程 零基础搞定周报与数据分析

使用WorkBuddy时需下达清晰指令,包括文件路径、输出格式和完整需求。典型场景如周报生成、Excel数据清洗与可视化,需注意指定去重列和输出格式,避免打断大文件处理。定时任务可自动化抓取新闻,轻量模型和Ask模式可节省积分。

CC压缩机制之toolResultBudget源码实现原理技术深度解读
AI教程 · 2026-08-05

CC压缩机制之toolResultBudget源码实现原理技术深度解读

toolResultBudget机制在每次模型请求前自动执行,检查单个API-levelusermessage中tool_result总量是否超过200K字符,若超则将最大的工具结果落盘并替换为预览,以降低上下文噪音。该机制位于压缩流水线最前端,在microcompact之前执行,确保后续压缩更高效。