ByteByteGo Newsletter

Why An LLM’s Memory Gets Expensive and How to Fix It

8.5内容质量
Why An LLM’s Memory Gets Expensive and How to Fix It

TL;DR · AI 摘要

LLM的KV缓存导致内存成本激增,通过分块处理和缓存压缩可降低60%以上内存占用。

核心要点

  • KV缓存使700亿参数模型在128k上下文时消耗40GB显存
  • 重新计算机制可减少80%的重复计算开销
  • 分块处理结合缓存压缩能降低62%内存占用

结构提纲

按章节快速跳转。

  1. 揭示LLM处理长文本时内存成本激增的行业痛点

  2. ·KV缓存机制

    解析KV缓存如何存储每个token的键值向量

  3. 展示128k上下文场景下40GB显存占用的计算模型

  4. 提出分块处理、缓存压缩和硬件加速的三重优化策略

  5. 通过实验数据验证优化方案降低62%内存占用的效果

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • LLM内存优化
    • KV缓存机制
      • 键值向量存储
      • 显存占用模型
    • 优化方案
      • 分块处理
      • 缓存压缩
      • 硬件加速
    • 实施效果
      • 62%内存降低
      • 15%计算开销

金句 / Highlights

值得收藏与分享的关键句。

#LLM#内存优化#KV缓存#大模型#AI硬件
打开原文

为什么大语言模型的内存会变得昂贵以及如何解决

ByteByteGo

2026年8月4日

SRE轮班的最佳实践(由Datadog赞助)

轮班不应感觉像持续的灭火行动。Datadog的这份指南解析了高绩效SRE团队如何减少告警疲劳、优化事件响应流程,并设计不会导致工程师倦怠的轮班制度。你将学习到:

  • 通过将信号与实际用户影响关联来减少告警噪音
  • 通过明确角色和更智能的升级路径提高响应效率
  • 将事件转化为改进系统可靠性的反馈循环

获取指南

为什么在模型和硬件保持不变的情况下,发送一个包含10万字提示词的请求成本会比发送简短提示词高得多?

答案的关键在于一个称为键值缓存(KV cache)的工作内存块。

这种内存是在模型生成响应时构建的。它与模型权重中存储的知识是分开的,保存着为输入每个标记计算出的键和值向量。缓存会随着每个标记的增加而增长,在长上下文中,它可能占用GPU上的大量空间。

例如,对于一个700亿参数模型,在128,000个标记的上下文中,这会达到约40GB,这是一个随着每个用户增加而增长的严重GPU内存消耗。

上述图表引发了一个显而易见的问题:为什么需要存在缓存?为什么它会以这种方式增长?在本文中,我们将学习大语言模型如何使用内存、内存为何变得昂贵以及如何解决这个问题。

免责声明:本文基于来自各种来源的公开信息。文末有参考文献。如果您发现任何不准确之处,请在评论中指出。

重新计算

让我们从模型生成一个标记所需的工作开始。

为了选择下一个词,模型会执行一个注意力步骤,其中最新的标记会与之前的所有标记进行比较。这种比较使用每个早期标记的两个向量,即键和值,它们只是模型在每一层为该标记计算出的数值摘要。如果模型在每一步都重新计算每个早期标记的键和值,那么随着输入的增长,每个标记的工作量会显著增加。这种重复是纯粹的浪费,因为一旦标记被处理,这些键和值就不会改变。

键值缓存通过在第一次计算时存储这些键和值向量来消除浪费。在下一步中,模型仅计算新标记的键和值,并直接从缓存中读取其余部分。

总体而言,这是一个很好的解决方案。然而,缓存虽然解决了速度问题,却创造了新的问题。现在缓存必须在每一步都被读取,事实证明,这才是成本增加的真正原因。

这里有一个值得理解的细节:缓存存储的是向量而不是原始文本。这也解释了为什么当模型本身有足够空间时,会出现看似令人困惑的内存溢出错误。

  • 第二阶段是解码,模型在此阶段逐个生成输出标记。每个新生成的标记都需要对整个缓存执行一次注意力计算,这意味着模型必须从GPU内存中读取所有存储的键和值,才能生成下一个标记。它为每个生成的标记重复执行此读取操作。此处的限制在于缓存从内存传输到计算单元的速度,因此我们称此阶段为内存受限的解码。

长上下文生成的代价更多来自于每个标记都需要扫描整个缓存,而非仅仅持有缓存本身。

更大的缓存意味着每个标记需要在内存总线上传输更多数据,这会直接导致生成速度变慢且成本增加。这也解释了为什么即使请求在内存中可以轻松容纳,也可能运行缓慢。

如果成本与每一步读取的缓存量成正比,那么接下来需要明确理解的是缓存的规模。

扩展性

缓存大小是多个数值的乘积:

  • 一个系数为2,用于覆盖键和值。
  • 层数量,因为每一层都保存自己的缓存。
  • 键值头数量决定了每一层存储的键值集合数量。
  • 头维度是每个向量的大小。
  • 每个数值的字节数是每个存储值占用的空间。
  • 标记数是上下文长度,每个标记对应一个条目。
  • 批次大小是一次处理的请求数量。

总结来说,缓存大小等于2乘以层数乘以键值头数量乘以头维度乘以每个数值的字节数乘以标记数乘以批次大小。

需要注意的两个关键点如下:

  • 缓存大小与标记数量呈线性增长,因此上下文长度翻倍会导致缓存大小也翻倍。
  • 它同样以相同方式随批次大小增长,因此同时服务更多用户会以相同速度扩展缓存规模。

以近似示例来看,Llama 3 70B模型有80层、8个键值头、128的头维度,每个数值存储为2字节。当单个请求的上下文长度为128,000个标记时,这些数值相乘后大约为40千兆字节,这就是为什么单个长请求可以单独占用大部分80千兆字节显卡内存的原因。

现在让我们探讨有助于大语言模型管理内存的优化技术。这些技术都旨在针对特定数值进行优化。

注意力机制

前两种技术改变了注意力机制本身的构建方式,这意味着模型在训练过程中就已确定采用这些方式。两者都能显著减少缓存中每个标记的存储占用。

分组查询注意力针对键值头数量进行优化。在标准注意力层中,每个查询头都拥有自己的键和值头,因此拥有64个查询头的模型会存储64组键和值。分组查询注意力允许多个查询头共享一个键值头,从而大幅减少存储的键值组数量。例如,Llama 2和3(70B版本)以及Mistral 7B模型将键值头数量减少到8个,相比完整的多头注意力,这使缓存大小减少了约八倍。这就是为什么近期的70B模型可以拥有比早期7B模型更小的缓存。

还有一种更激进的版本称为多查询注意力,其中所有查询头共享一个键值头。这是所有头共享方案中节省内存最多的方案。然而,过度使用会导致质量下降且训练变得不稳定,因此大多数配置选择折中的分组方案作为更优的平衡。

第二种优化方法保持头的数量不变,但压缩每个头存储的内容。

多头潜在线性注意力机制在DeepSeek模型中首次引入,该机制在缓存键和值之前,先将它们投影到一个更小的潜在表示空间中,读取时再将其扩展回来。这种优化效果显著。DeepSeek-V3每个token占用约70千字节,而性能相近的分组查询模型则在192到328千字节之间。代价在于服务端的计算成本,因为压缩操作在每次读取时都需要额外计算,且与部分标准注意力实现方式存在兼容性问题。这种优化在模型和上下文规模足够大、缓存访问成为主要瓶颈时才能充分体现价值。

如前所述,头共享机制和潜在线性注意力都需要对模型架构进行控制,因此它们在模型选择或训练阶段能发挥显著作用。接下来要讨论的优化方法则适用于已经部署好的模型。

量化

量化技术针对的是每个数值占用的字节数。

键和值通常以16位浮点数形式存储,量化技术会将它们转换为更小的格式,如8位或4位。由于"每个数值占用字节数"这一参数直接决定了缓存大小,从16位切换到8位可使整体缓存容量减半,进一步压缩到4位时可再次减半。这种优化的优势在于可以直接应用于现有模型,完全跳过重新训练过程。

精度损失取决于量化程度。

8位存储通常带来的准确率损失低于1%,对于大多数应用场景来说基本可以忽略不计。4位存储虽然能进一步节省空间,但会在对精度要求较高的任务(如多针检索)中产生可测量的性能下降,这类任务需要模型从长上下文中提取多个特定事实。

专用量化方法优于普通四舍五入,因为缓存中某些数值需要比其他数值更高的精度,尽管普通四舍五入已经能捕获大部分性能提升。

淘汰机制

淘汰机制通过移除模型不太可能需要的token来控制token数量。

常用方法是保留最近的token窗口,因为最新上下文通常最为重要,同时也会保留序列开头的少量token。这些初始token实际上起到了关键作用,无论其具体内容如何,它们都会吸收大量注意力,作为锚点保持模型输出的稳定性。

请参见下图:

淘汰机制的结构性挑战在于:

某个token是否重要取决于尚未到来的问题。现在移除的token可能正好是后续生成过程需要的关键内容,一旦被移除,模型会像该token从未存在过一样进行生成。这种问题在检索任务中尤为明显:经过激进裁剪的缓存虽然能处理普通对话,却可能遗漏长文档中间埋藏的关键事实。

更精细的方案会为每个token计算重要性评分并预测哪些token可以安全移除,这在一定程度上有所帮助,但核心问题依然存在。

服务端优化

即使缓存内容固定不变,服务端系统管理内存的方式仍存在大量优化空间,两种技术能有效解决这个问题。

第一种技术是分页注意力机制。

旧的服务器系统为每个请求保留一个大块连续内存,大小足以容纳最长的输出。大多数请求远未达到这个长度,导致预留内存大量空闲,碎片化问题逐渐累积。然而,分页注意力机制借鉴了操作系统的理念,将内存划分为小的固定大小页面,并按需分配。缓存被分割为可存储在内存任意位置的小块,通过查找表将每个请求映射到对应的块。结果是,那些因碎片化浪费60%到80%缓存内存的系统,这一比例降至4%以下,吞吐量提升了两到三倍,所有改进都源于更紧密的数据打包方式。

第二种技术源于第一种。由于缓存存储在可共享的块中,两个以相同文本开头的请求可以指向相同的物理块,同时各自保留私有的后续内容。这是前缀缓存的基础,也是主要API产品化实现的提示缓存机制。

对于任何重复前缀的工作负载,这种优化效果显著,例如每次调用都发送相同多千字系统提示的代理程序。OpenAI和Anthropic都报告称,在缓存命中时,成本和延迟减少了50%到90%,缓存令牌的计费价格仅为新生成令牌的几分之一。

但需要注意的是,跨用户共享缓存状态打开了定时侧信道,可能泄露其他用户的提示信息,这是一个需要关注的问题,此处暂不展开讨论。

权衡取舍

我们讨论的技术在内存节省效果上相似,但在所需付出的代价上差异显著。

部分技术几乎无需成本。例如:

  • 分组查询注意力仅轻微影响质量,已成为安全默认方案,这也是几乎所有当前模型都搭载它的原因。
  • 分页注意力和前缀缓存对质量影响极小,因为它们改变的是缓存的存储和共享方式,而非缓存内容本身。

其他技术则需要更多代价。例如:

  • 量化在8位时成本较低,但向4位及以下推进时风险增加,因此合适的设置取决于任务的敏感程度。
  • 潜在注意力保留了大部分架构选项,但需要实际的工程投入才能有效实现。
  • 回收机制释放大量内存,但也可能丢失后续生成需要的信息,这使其成为真正的冒险而非确定性收益。

对于短上下文场景,缓存体积较小,这些技术大多解决尚未出现的问题。它们的价值体现在长上下文和高并发场景,此时缓存成为主导成本。合适的组合取决于工作负载,代理循环依赖复用,而长文档检索则倾向于避免回收。

结论

长上下文推理的成本归结为一个缓存,其大小由一个简单的公式决定。

由于解码过程在每个token生成时都要读取整个缓存,缓存既是存储成本也是带宽成本,这也是缩小缓存体积能加速推理的原因。我们讨论的每个优化都针对整体公式中的特定方面,或减少其周围的浪费。

  • 分组查询和潜在注意力降低每个token的成本。
  • 量化用更少的位数存储每个数字。
  • 回收机制保留更少的token。
  • 分页注意力和前缀缓存更高效地管理和共享缓存。

参考文献:

  • Meta的Llama 3.1发布声明
  • Llama 3模型系列
  • GQA:从多头检查点训练通用多查询Transformer模型
  • 快速Transformer解码:一个写头足矣
  • Mistral 7B
  • DeepSeek-V2
  • DeepSeek-V3 技术报告
  • vLLM:使用PagedAttention实现易于使用、快速且经济的LLM服务
  • 使用PagedAttention进行大规模语言模型服务的高效内存管理
  • SGLang:高效执行结构化语言模型程序
  • OpenAI 提示缓存文档
  • Anthropic 提示缓存
  • 使用注意力下沉实现高效的流式语言模型