推理时显存构成
💾 推理时,显存主要被哪两大部分占据?¶
在LLM推理的显存分配中,有两个绝对的主角,它们占据了绝大部分的GPU内存,分别是模型权重和KV Cache。其他部分如临时激活值、框架开销等虽然存在,但通常占比很小(<10%),且可以通过技术手段优化。理解这两大块是进行一切推理优化的基础。
① 模型权重
这是推理的入场券。无论你问一个字还是万字长文,整个模型的参数都必须安静地躺在显存里。对于FP16精度的7B模型,这部分固定占用14 GB,是静态的、不可压缩的底线(除非量化)。
② KV Cache
这是推理过程的记忆成本。自回归生成每产生一个token,模型的所有层都要把当前token对应的Key和Value向量追加保存,以便后续token不再重算历史。它的显存占用会随着序列长度和batch size线性增长,是长文本推理中最凶猛的“吞金兽”。在短文本下它可能只占5%,但在超长上下文(如32k)时,它甚至可以超过权重本身,达到60%以上。
📊 占比随上下文长度的变化表:
💡 核心洞察:上下文长度是决定显存压力模式的核心变量。在短对话中,你的钱主要花在“买模型”上;而在长文档分析中,你的钱主要花在“维持记忆”上。优化长文本推理,首要目标是砍向 KV Cache。
🧠 什么是 KV Cache?为什么推理需要它?¶
KV Cache 是 Transformer 模型在自回归生成时采用的一种关键优化技术,其本质是用空间(显存)换取时间(计算)。
为什么需要 KV Cache?
自回归生成每次只输出一个token。在计算这个新token的注意力时,它需要与所有历史token进行交互。如果没有KV Cache,每生成一个新token,都要将前面所有token的Key和Value重新计算一遍。这会导致:
-
计算量爆炸:总计算复杂度变为 O(N3),其中N是序列长度。
-
时间灾难:生成一篇千字文章可能需要几个小时。
-
显存浪费:重复计算会产生大量中间激活值,极易导致OOM。
KV Cache的工作原理 在生成第一个token(处理完prompt)时,我们将prompt中所有token的Key和Value向量缓存起来。之后每生成一个新token,只需计算这个新token的Query、Key和Value,然后从缓存中读取所有历史token的K和V,完成注意力计算。最后,将新token的K和V追加到缓存中。这样,每一步的计算量从 O(N2) 降为 O(N),总计算复杂度从 O(N3) 降为O(N2),极大地提升了生成速度。
没有KV Cache会怎样?
-
速度极慢,无法实用。
-
显存爆炸,因为需要同时保存所有历史token的完整前向计算图。
💡 这就像作家写文章:每写一个字,都需要回顾前文所有内容以保持连贯。KV Cache就是作家的“短期记忆”,让他不用每次都从头读起。
🧮 KV Cache 的显存占用公式是怎样的?各变量含义?¶
对于标准多头注意力(MHA),KV Cache总显存的计算公式如下:

各变量详解:
-
2:代表每个注意力头需要同时存储 Key 和 Value 两个张量,两者大小相同。 -
L:Transformer 的层数(Layers)。每一层都有独立的K、V投影,缓存互不共享。 -
B:批处理大小(Batch Size)。不同推理请求的KV Cache是严格隔离的,因此显存会随并发数线性增长。 -
S:序列总长度(Sequence Length),即prompt长度 + 已生成token数。这是动态变量,在生成过程中从0开始线性增长,是长文本推理时最可怕的乘法因子。 -
H:注意力头数(Num Heads),对于 MHA 而言,K/V 头数与Q头数相同。

📈 为什么 Decode 阶段 KV Cache 会线性增长?¶
在Decode阶段,每生成一个新token,模型都会在每一层的Key和Value缓存末尾追加当前token对应的向量。由于每个token贡献的K、V大小是固定的(由模型维度决定),而生成过程是顺序进行的,因此随着越来越多的token被生成,缓存中存储的向量总数线性增加,KV Cache的大小也就线性增长。直到生成结束(遇到EOS或达到最大长度),这个线性增长过程才会停止。
💡 这就像你在不断收集邮票,每收集一张新邮票就往集邮册里插一页,册子越来越厚。
⚡ Prefill 阶段和 Decode 阶段的显存特点有何不同?¶
Prefill 阶段(预填充)
- 发生了什么:一次性并行处理整个prompt序列,计算所有token的注意力,并生成第一批Key和Value缓存。

- 显存特点:会产生一个巨大的、与序列长度平方成正比的注意力得分矩阵(如果未使用FlashAttention),形成瞬时的显存高峰。KV Cache在此阶段被创建并写入。
Decode 阶段(解码生成)
-
发生了什么:进入“一个字一个字”的生成循环。每次只处理一个新token,计算它的Query,然后从显存读取所有历史Key和Value完成注意力计算。
-
计算特点:显存带宽密集型。每次计算量极小,但需要从显存读取整个模型权重和全部KV Cache。计算单元大部分时间在等待数据,GPU利用率低。
-
显存特点:临时激活值占用很小且稳定,但KV Cache在持续、线性地增长,成为显存占用的主体。
🏁 一句话总结:Prefill是场短跑,拼的是谁算得快(算力);Decode是场马拉松,拼的是谁补给线通畅(带宽)。
📉 推理时,模型权重的量化(INT8/INT4)能节省多少显存?¶
权重量化直接减少了模型参数的存储量,但对总显存的降幅取决于权重在总显存中的占比。
-
FP16(基准):每个参数2字节。7B模型权重约14 GB。
-
INT8:每个参数1字节。权重显存降为原来的 1/2,7B模型约7 GB。
-
INT4:每个参数0.5字节,加上少量量化常数(scale, zero-point),7B模型实际约3.7~4.5 GB。权重显存降为原来的 1/4 左右。
总显存降幅分析:
-
短文本、小batch场景:权重占绝对主导。INT4量化后,总显存可能从18 GB降至7.5 GB,降幅约58%。
-
长文本、大batch场景:KV Cache成为显存主体。例如13B模型处理长文本时,INT4量化可能只让总显存从70 GB降至50.5 GB,降幅仅约28%。
💡 关键洞察:权重量化节省的是“存储空间”,但对Decode阶段的“带宽瓶颈”缓解有限,因为量化权重在使用时通常要反量化回FP16计算,读入计算单元的数据量变化不大。它最大的价值是让模型能跑在更小显存的设备上。
🧩 如果推理时用了 GQA,KV Cache 会减少多少?¶
GQA(分组查询注意力)的核心思想是让多个Query头共享一组Key和Value头,从而从源头上减少需要缓存的K/V头数。
-
MHA:有H个独立的K/V头。
-
GQA:将H个Q头分成G组,每组共享一个K/V头,因此只需要存储G个K/V头。

实例:LLaMA-7B 的 H=32,若使用 G=4 的 GQA,则 KV Cache 缩减为原来的 4/32=1/8。若原来在2048长度下KV Cache为1 GB,GQA后仅需约0.125 GB。
🚀 GQA以极小的性能损失,将KV Cache缩小到原来的几分之一,是现代长文本模型(如LLaMA-2/3)的标配。
📊 MHA、MQA、GQA 的 KV Cache 大小对比?¶
下表清晰地展示了三种注意力机制在KV Cache大小上的差异(假设总头数H=32):
-
MHA:像每个专家(Q头)都有自己的专属资料库(K、V头),资料库太多,占地方。
-
MQA:所有专家共用一个资料库,最省地方,但专家们容易打架。
-
GQA:把专家分成几个小组,每组共享一个资料库,兼顾了表达力和存储成本。
🚀 MLA(多头潜在注意力)是如何压缩 KV Cache 的?¶
MLA(Multi-head Latent Attention)是DeepSeek-V2等模型中使用的一种极致KV Cache压缩技术,其核心思想是低秩压缩。
原理:

对比GQA:
-
GQA是从“宽度”上缩减,通过共享K/V头来降低头数。
-
MLA是从“高度”上压缩,直接降低每个token的K/V表征维度,压缩更为彻底。
代价:
-
潜向量维度 dl 过小会丢失信息,影响模型精度。
-
需要额外的矩阵乘法来恢复K和V,但计算量增加不大。
💡 MLA将KV Cache从“存完整相片”变成“存压缩缩略图+一套还原算法”,大幅降低了长文本推理的显存门槛。
🧪 推理时的临时激活值主要出现在哪个阶段?占用大吗?¶
临时激活值是指神经网络在前向传播过程中产生的中间输出张量,它们只在前向计算时短暂存在,计算完成后即可释放。
主要出现阶段
临时激活值在 Prefill 阶段达到峰值。因为在 Prefill 阶段,模型一次性并行处理整个 Prompt 序列。最大的临时激活值通常是注意力得分矩阵 QKT,其形状为 [batch, heads, S, S],其中 S 是 Prompt 长度。当 S 很大时(如 32k),这个矩阵的大小会呈平方级膨胀。即使使用了 FlashAttention 等优化技术,通过分块计算避免了存储完整矩阵,但仍需要为每个分块分配临时缓冲区。
在 Decode 阶段,每次只处理一个新 token,矩阵运算规模极小(如 [1, 1, 1, S] 的注意力),产生的临时激活值非常小且相对稳定,通常不是显存瓶颈。
占用大吗? 对于短文本推理,临时激活值占用很小(通常小于 1 GB),可以忽略。但在长文本 Prefill 时,如果不加以优化,注意力得分矩阵可能导致瞬时显存高峰,甚至 OOM。FlashAttention 等技术的核心价值就在于通过分块和重计算,将这部分瞬时的 O(S2) 显存开销降为 O(S)。
📦 推理时 batch size 增大,主要增加哪部分显存?¶
batch size 增大时,增加最显著的部分是 KV Cache。因为每个独立的推理请求都需要维护自己的一份 KV Cache,不同请求之间的 KV Cache 完全隔离,无法共享。因此,KV Cache 的显存占用与 batch size 是严格的线性关系。当 batch size 从 1 增加到 8 时,KV Cache 的显存也恰好变为原来的 8 倍。
此外,在 Prefill 阶段,大 batch 会同时产生更多的临时激活值,也会增加一部分显存压力,但这部分通常可以通过 FlashAttention 和算子融合来优化。模型权重是所有 batch 共享的,不会随 batch size 增加而增加。
结论:限制在线服务吞吐量的核心瓶颈,往往就是 KV Cache 的容量。要提升 batch size,就必须降低单个请求的 KV Cache 大小(如使用 GQA、KV Cache 量化)。
🐉 推理长文本(如 128K)时,显存瓶颈通常是什么?¶
推理 128K 长度的文本时,显存瓶颈毫无疑问是 KV Cache。
以 LLaMA-7B 为例(MHA 32 头,FP16),单个 token 的 KV Cache 在 32 层下约 0.5 MB。对于 128K tokens,单个请求的 KV Cache 就需要约 64 GB 显存!这已经接近甚至超过了一张 80GB A100 的可用显存。此时,模型权重(约 14 GB)反而成了“小头”。任何并发请求都会立即导致 OOM。
因此,处理超长文本时,必须使用 GQA(减少 K/V 头数)、MQA、MLA(低秩压缩)或 KV Cache 量化(INT8/INT4)等技术来压缩 KV Cache,否则推理根本无法进行。
⚖️ 为什么推理时模型权重通常可以量化,而训练时不行?¶
推理时权重量化可行:因为推理是一个纯前向传播过程。量化引入的微小精度损失,在累加后通常不会对最终输出质量产生灾难性影响。而且,权重量化带来的显存节省和带宽提升(尤其是对 Decode 阶段这种带宽瓶颈任务)收益巨大。即使有轻微精度下降,往往也在可接受范围内。
训练时权重量化困难:因为训练涉及反向传播和参数更新。量化后的离散权重值无法直接接收连续的梯度更新,因为梯度是微小浮点数,会被量化误差淹没,导致优化器无法有效工作。如果强行在训练中使用低位量化,模型很难收敛,或者需要非常复杂的量化感知训练(QAT)技巧来模拟误差,成本极高。因此,训练时通常只在优化器状态上用低位精度(如 8-bit Adam),而模型权重本身仍保持高精度(FP16/BF16),以便进行平滑的参数更新。
🔗 推理时使用流水线并行(PP)能减少单卡显存吗?为什么?¶
能减少单卡显存,但减少的是模型权重和 KV Cache 的存储负担。
流水线并行将模型的不同层分配到不同的 GPU 上。例如,一个 32 层的模型,GPU0 负责第 1-16 层,GPU1 负责第 17-32 层。
-
权重显存:每张 GPU 只存储自己负责的那部分层的权重,因此权重显存降为原来的 1/PP。
-
KV Cache:同样,每张 GPU 只存储自己负责的层的 KV Cache,总量也降为原来的 1/PP。
-
中间激活:在推理时,中间激活(hidden states)需要在 GPU 间通过点对点(P2P)通信传递。每张 GPU 的激活峰值也相应降低。
与张量并行(TP)相比,PP 的通信发生在层间边界,通信量较小,因此对跨节点网络带宽的要求不高,适合跨机器扩展。但 PP 的缺点是可能引入流水线气泡,且在 batch size 较小或模型层数不多时效率较低。
📱 端侧设备(手机)推理时,显存限制一般在什么量级?¶
当前主流旗舰手机(如搭载骁龙 8 Gen 3 或 A17 Pro 的设备)通常配备 12-16 GB 的统一内存(CPU 和 GPU 共享)。这意味着操作系统、其他应用和模型需要共同瓜分这有限的资源。
实际可供模型使用的内存通常只有 4-8 GB。因此,要在手机上运行大模型,必须依赖:
-
极限权重量化:使用 INT4 甚至 INT3/INT2 量化,将 7B 模型的权重压缩到 2-4 GB。
-
小模型:直接部署 1.5B-3B 的轻量模型。
-
KV Cache 压缩:限制最大生成长度,使用 GQA 和 KV Cache INT8 量化。
-
内存映射与卸载:利用
mmap等技术支持从闪存动态加载模型层,减少内存峰值。
🔁 为什么在多轮对话中,KV Cache 不断累积会导致 OOM?¶
在多轮对话中,每一轮的 Prompt 实际上是由所有历史对话拼接而成的。模型为了保持上下文连贯性,需要保留所有历史轮次的 KV Cache。随着对话轮次增多,缓存的 token 数线性增长,KV Cache 的体积也会线性膨胀。当历史积累的 KV Cache 加上当前轮次的模型权重和临时激活值超过了 GPU 物理显存上限时,就会发生 OOM。
示例:假设一轮对话平均产生 200 tokens,经过 50 轮对话,KV Cache 就累积了 10,000 tokens 的历史。如果模型是 7B MHA,KV Cache 可能已经膨胀到数 GB,最终导致新轮次的请求因显存不足而失败。
🧹 如何清理或回收 KV Cache 以节省显存?¶
在多轮对话或长文本处理中,主动管理和回收 KV Cache 是防止 OOM 的关键。常见策略有:
-
滑动窗口 (Sliding Window):只保留最近 N 个 token 的 KV Cache,超出窗口的旧 token 对应的缓存被直接丢弃。这是最简单直接的方法,但会丢失早期上下文。
-
StreamingLLM:保留开头的几个“注意力汇聚点(Attention Sinks)”token,加上滑动窗口内的 token,丢弃中间部分的 KV Cache。这样既控制了缓存大小,又维持了注意力机制的稳定性。
-
H2O (Heavy-Hitter Oracle):根据累积的注意力分数,动态识别出“重击手”token,只保留这些关键 token 的 KV Cache,淘汰掉不重要 token 的缓存。这是一种智能的淘汰策略。
-
前缀缓存 (Prefix Caching):多个请求共享的相同前缀只存一份。当所有引用该前缀的请求完成后,该部分缓存被回收。
-
手动重置:在对话应用中,当用户主动“开启新对话”或检测到话题彻底转换时,可以调用接口强制清空当前会话的 KV Cache,从零开始。
🚀 什么是“前缀缓存”(Prefix Caching)?如何节省显存?¶
前缀缓存是一种智能的 KV Cache 复用技术。在许多应用场景中(如多轮对话、批量评估),大量请求的 Prompt 开头部分是完全相同的(例如通用的 System Prompt)。前缀缓存检测到这种相同的前缀后,会对该前缀的 KV Cache 只计算并存储一次。后续所有具有相同前缀的请求,其页表可以直接映射到这些已缓存的物理页上,无需重新计算或重复存储。
显存节省效果:假设一个 System Prompt 有 1000 tokens,有 100 个并发请求。如果不使用前缀缓存,需要存储 100 份该前缀的 KV Cache,占用大量显存。使用前缀缓存后,这 100 个请求共享同一份物理缓存,显存占用降为原来的 1/100。同时,由于 Prefill 阶段也无需对这 1000 tokens 重复计算,TTFT(首Token延迟)也会大幅降低。
❄️ 推理时是否也可以使用激活值卸载?适用场景?¶
可以使用,但代价高昂。 推理时的激活值卸载指的是将 Prefill 阶段产生的部分中间激活值(如注意力矩阵)临时转移到 CPU 内存,需要时再加载回 GPU。
适用场景:仅在极端长文本推理且显存极度紧张时作为“兜底”方案。因为 Prefill 阶段会产生巨大的瞬时激活峰值,如果此时 GPU 显存不足,卸载可以避免 OOM,让推理得以进行。
代价:CPU 和 GPU 之间的数据传输受限于 PCIe 带宽(远低于 HBM 带宽),频繁的卸载和加载会严重拖慢推理速度,导致延迟飙升。因此,激活值卸载并非一种性能优化手段,而是一种用速度换显存的“求生”策略。在常规推理中,依靠 FlashAttention 等技术来降低激活值峰值是更优的选择。