大语言模型运行起来以后,很多人会发现一个奇怪的现象:模型参数明明已经经过4-bit、8-bit量化,GPU显存还是很容易被吃光。尤其是在长上下文、多用户并发的推理服务器上,显存消耗往往随着活跃请求数量和上下文长度迅速增加。
其中一个重要原因就是KV Cache。
KV Cache不是模型参数,也不是简单意义上的“聊天记录”。它保存的是Transformer自注意力计算过程中已经计算出来、后续生成过程还需要重复使用的Key和Value张量。模型在生成新token时,可以直接读取过去token对应的Key和Value,而不必每生成一个新token都重新计算整个历史序列。
理解这一点,就能解释为什么上下文越长、并发越高,KV Cache越容易成为GPU显存规划中的核心问题。
KV Cache到底保存什么
Transformer的自注意力机制会根据输入隐藏状态计算Query、Key和Value。
对于当前正在处理的token,模型需要Query来寻找与历史token之间的相关性,同时需要访问历史token对应的Key和Value。
如果没有KV Cache,那么模型每生成一个新token,都需要重新计算之前所有token的Key和Value。
假设已经有几千个历史token,那么生成下一个token时再次计算整个历史序列,会产生大量重复工作。
KV Cache的基本思路就是把已经计算过的Key和Value保存下来。
后续生成新token时,只需要计算新token产生的Key和Value,并把它们加入缓存,同时读取之前已经保存的数据参与注意力计算。
因此,KV Cache本质上保存的是“过去token在各个Transformer层中的Key和Value表示”。
它不是把模型的完整隐藏状态保存一份,也不是把整个Transformer中间结果全部缓存下来。
为什么一个token的KV数据并不只有几十或几百字节
原稿使用“一个token大约128字节”来估算显存,这是一个过度简化的算法。
KV Cache中,一个token对应的数据量与模型架构密切相关。
粗略来说,一个token需要在每个Transformer层保存Key和Value,而每层的数据量又取决于KV头数量、每个头的维度以及KV Cache使用的数值精度。
因此可以用一个更有意义的近似关系理解:
KV Cache容量
≈ 层数 × 2 × KV头数 × Head Dimension × Token数量 × 每元素字节数
这里的“2”来自Key和Value两部分。
这个关系比简单地说“一个token占多少字节”更重要,因为不同模型的层数、Attention结构、KV头数量和精度可能完全不同。
例如采用标准多头注意力MHA的模型,与采用Grouped Query Attention,也就是GQA,或者Multi-Query Attention,也就是MQA的模型,在KV Cache容量上可能有明显差异。
GQA为什么能减少KV Cache
这是理解现代LLM显存优化非常重要的一点。
传统MHA中,Query头和KV头数量通常相同。
而GQA会让多个Query头共享一组Key和Value头。
MQA则进一步减少KV头数量,让更多Query头共享KV。
因此,在模型参数量和上下文长度相近的情况下,GQA或MQA模型的KV Cache可能明显小于同等条件下的标准MHA模型。
这也是为什么不能简单按照“模型有多少参数”直接推算KV Cache。
模型参数量主要决定权重存储规模,而KV Cache则更多受到层数、KV头数量、Head Dimension、上下文长度、并发请求数量以及KV数据精度影响。
Prefill和Decode对KV Cache的意义不同
LLM推理通常可以从Prefill和Decode两个阶段理解。
Prefill阶段负责处理用户输入的prompt。假设用户一次提交5000个token,模型会对这批输入进行计算,并建立相应的KV Cache。
随后进入Decode阶段,模型一个token一个token地生成回答。
每生成一个新token,新的Key和Value会继续加入KV Cache,同时模型需要读取已经存在的历史KV数据。
因此,Prefill阶段主要是在建立缓存,而Decode阶段则不断使用和扩展缓存。
这也是为什么长prompt可能让首次响应时间,也就是TTFT,明显增加,而长时间生成则会持续增加活跃序列的KV Cache占用。
为什么并发比单个请求更加可怕
单个请求的KV Cache可能并不算特别夸张,但推理服务器通常不会只服务一个用户。
假设服务器同时运行几十甚至几百个活跃序列,每个序列又拥有几千甚至几万token的上下文,那么这些缓存会同时存在于GPU显存中。
因此,可以把KV Cache的总规模粗略理解为:
总KV Cache
≈ 单token KV容量 × 所有活跃序列的token数量
这里真正重要的是“活跃token总量”,而不是一个孤立的Batch Size数字。
例如两个系统都设置Batch Size为32,但一个系统中的32个请求平均只有500个活跃token,另一个系统中的32个请求平均拥有10000个活跃token,两者的KV Cache压力完全不同。
所以现代推理系统经常需要同时考虑最大并发序列数、最大batch token数、上下文长度和KV Cache容量。
为什么长上下文会迅速增加显存压力
上下文从2000 token增加到20000 token,并不意味着模型参数增加了。
但是每一个历史token都可能需要继续保留对应的Key和Value,因此KV Cache会随着活跃上下文token数量增加。
这也是长上下文模型与普通短上下文模型在推理成本上的一个重要区别。
当大量请求同时保持长上下文时,GPU显存中的KV Cache可能比模型权重更容易成为动态容量压力来源。
尤其是在在线服务中,一个请求可能持续几十秒甚至几分钟,生成过程中缓存不会立即释放。
如果系统还支持较大的最大上下文长度,就必须为这种最坏情况预留足够的缓存管理空间。
KV Cache和模型权重不是一回事
这是部署大模型时非常容易混淆的问题。
模型权重通常是相对稳定的。
例如一个模型加载完成以后,权重会长期驻留在GPU显存中。
KV Cache则是动态的。
用户来了,缓存增加;请求结束,缓存释放;上下文越长,缓存越大;并发越高,缓存需求越大。
因此,量化模型权重并不意味着KV Cache也自动按照完全相同的比例缩小。
例如一个模型的权重可以采用4-bit存储,但KV Cache可能仍然采用FP16、BF16或者其他专门的数据格式,具体取决于模型、硬件和推理框架。
KV Cache也可以采用8-bit或者更低精度的方案,但这属于另外一层优化,需要考虑精度、算子支持和实际性能。
GPU显存为什么比CPU内存更重要
在高性能推理中,KV Cache通常希望尽量放在GPU显存中,因为Decode阶段会频繁访问这些数据。
GPU HBM提供很高的内存带宽,可以让GPU快速读取权重和KV Cache。
但HBM容量是有限的。
如果活跃请求太多,KV Cache无法全部放进GPU显存,就需要进行缓存淘汰、请求调度或者将部分数据转移到其他内存层级。
问题在于,GPU显存之外的存储层级通常具有更高的访问延迟,而且带宽也可能明显低于GPU本地HBM。
因此,“把KV Cache放到CPU内存”不是免费获得容量,而是用容量换取访问性能和复杂度。
NVMe SSD不是GPU显存的简单替代品
原稿把NVMe SSD作为显存不足后的持久化存储方案,这个说法容易让读者产生错误理解。
NVMe SSD适合数据持久化和大容量存储,但它与GPU HBM在访问延迟和带宽特征上存在巨大差异。
对于需要在Decode过程中频繁访问的活跃KV Cache,把数据大量放到SSD并不能简单地等价替换GPU显存。
实际系统可能使用CPU内存进行offload,也可能通过特定缓存机制管理部分数据,但是否值得这么做必须结合访问模式和性能目标测试。
SSD更适合作为数据存储层,而不是把整个活跃KV Cache当作一个可以无代价换出的“显存扩展”。
Paged KV Cache解决的不是数学容量问题
现代推理框架经常使用Paged KV Cache或者类似的分页式内存管理机制。
它解决的重要问题之一是如何更加灵活地管理不同请求的KV Cache。
传统的连续内存分配方式可能因为不同请求的长度不断变化而产生碎片。
一个请求结束后释放出来的空间未必能够直接满足另一个请求的连续空间需求。
Paged KV Cache可以把缓存拆分成更小的块,根据活跃请求动态管理这些块。
这样可以提高GPU显存的利用效率,并降低由于连续内存分配造成的碎片问题。
但它并不会改变一个模型在特定架构、精度、层数和上下文长度下所需要的基础KV数据量。
换句话说,分页管理主要是在解决“怎么把显存用得更有效率”,而不是凭空让KV Cache变小。
Continuous Batching为什么和KV Cache关系密切
在线LLM服务的请求不是同时到达,也不是同时结束。
如果采用传统静态Batch,把请求固定组合成一个批次,某些请求生成完成之后,GPU资源可能无法立即被新的请求充分利用。
Continuous Batching允许推理系统根据活跃请求动态调整执行批次。
新的请求可以加入,已经完成的请求可以退出。
这样能够提高GPU利用率和整体吞吐量。
但Continuous Batching同时也意味着推理调度器必须动态管理每个请求的KV Cache,因此KV Cache管理能力直接影响在线推理系统的容量和稳定性。
这也是现代推理框架为什么越来越重视Paged KV Cache、连续批处理和缓存调度。
KV Cache为什么会影响Token生成速度
KV Cache并不只是一个“显存容量问题”,它也直接影响Decode性能。
生成一个新token时,模型需要读取过去的Key和Value参与注意力计算。
随着上下文变长,需要处理的历史KV数据也会增加。
在某些模型和工作负载下,Decode阶段可能受到内存带宽和KV Cache访问效率的明显影响。
这也是为什么一块拥有更高理论计算能力的GPU,不一定就能按照同样比例提高Token/s。
如果当前工作负载主要受内存访问限制,那么增加Tensor Core计算能力未必能够带来等比例收益。
因此分析LLM推理速度时,不能只看GPU TFLOPS,还应该观察HBM带宽、KV Cache访问、模型权重读取、Kernel执行效率、并发度和上下文长度。
多GPU并不会自动解决KV Cache问题
当模型或者KV Cache需要跨GPU管理时,GPU之间的数据通信就进入了系统设计范围。
NVLink、NVSwitch、PCIe以及跨节点网络具有不同的带宽和延迟特征。
但是不能简单地说“使用NVLink以后KV Cache带宽提高3到5倍”。
具体收益取决于GPU型号、拓扑结构、并行方式、通信模式以及推理框架。
如果采用Tensor Parallel等模型并行策略,每张GPU通常只保存与自身计算分工相关的一部分数据,但同时需要进行必要的GPU间通信。
如果采用Data Parallel,让不同GPU分别服务不同请求,则每个GPU可以拥有独立的模型副本和本地KV Cache,通信模式又完全不同。
因此,多GPU架构需要先确定并行策略,再讨论KV Cache到底如何分布。
Prefix Cache又是另一种优化
如果大量请求共享相同的长前缀,例如企业内部知识库、固定系统提示词或者重复的文档上下文,那么每次都重新进行Prefill会浪费大量计算。
Prefix Cache可以复用已经计算过的前缀KV Cache,从而减少重复Prefill工作。
但Prefix Cache和普通KV Cache不能混为一谈。
普通KV Cache主要服务于当前活跃请求的上下文。
Prefix Cache则试图把可以复用的前缀KV保存下来,在后续请求出现相同前缀时重新利用。
它的收益高度依赖工作负载。如果请求之间没有可复用的精确前缀,那么缓存容量再大也无法创造出大量命中。
因此部署Prefix Cache时,需要同时观察前缀命中率、命中token数量、Prefill耗时、GPU显存成本以及端到端TTFT。
怎样估算实际KV Cache需求
工程上不能只问“这个模型是多少B”。
至少需要知道模型的层数、KV头数量、Head Dimension、KV Cache精度,以及同时存在多少活跃token。
一个比较实用的估算思路是:
单token KV容量
≈ 层数 × 2 × KV头数 × Head Dimension × 每元素字节数
总KV Cache容量
≈ 单token KV容量 × 活跃token总数
实际部署时还需要考虑内存管理开销、分页粒度、框架预留空间以及其他GPU显存占用。
例如模型权重、CUDA运行时、计算workspace、通信buffer等都需要显存。
所以不能把GPU总显存全部分配给KV Cache。
为什么不能用一个固定百分比判断显存是否够
不同模型、不同GPU和不同推理框架的显存布局差异非常大。
有人可能认为“KV Cache占显存超过某个百分比就危险”,这种固定阈值通常没有太大意义。
真正应该关注的是系统是否还能稳定容纳目标并发量和上下文长度,以及在接近容量上限时是否出现请求排队、缓存淘汰、offload、OOM或者P99延迟恶化。
在线服务还应该观察TTFT、ITL、整体Token/s、请求吞吐量、P50、P95和P99延迟。
如果增加并发以后GPU利用率上升,但P99延迟同时明显恶化,那么单纯继续增加并发并不是优化。
KV Cache优化应该从哪里开始
如果显存压力主要来自KV Cache,第一步不是盲目购买更大显存GPU,而是确认到底是哪一项导致缓存增长。
首先统计模型实际上下文长度和输出长度。
然后观察活跃序列数量和活跃token数量。
接下来确认模型采用MHA、GQA还是MQA,以及KV Cache使用什么精度。
如果工作负载存在大量重复前缀,可以评估Prefix Cache。
如果内存碎片明显,可以检查Paged KV Cache以及推理框架的缓存分配策略。
如果系统存在大量短请求,则应该优化调度和Continuous Batching。
如果请求确实需要超长上下文,则需要重新计算GPU显存容量,而不是期待某一个软件参数解决容量问题。
对于多GPU系统,则需要进一步确认模型并行、数据并行以及GPU互联拓扑。
KV Cache之所以成为大模型推理中的核心问题,是因为它把“模型大小”之外的另一个变量带进了GPU显存规划:用户正在使用多少上下文。
模型权重决定了模型本身需要多少固定显存,而KV Cache则随着活跃请求不断变化。上下文越长、并发越高,缓存越大;采用GQA或MQA可以降低KV规模,使用更低精度的KV Cache可以进一步压缩存储需求,Paged KV Cache和Continuous Batching则能够改善缓存管理和GPU利用效率,Prefix Cache可以减少重复前缀的Prefill计算。
因此,大模型推理的显存规划不能只看“多少B参数、用了几bit量化、GPU有多少GB显存”。真正需要计算的是模型权重、运行时开销以及目标并发下的活跃KV Cache总量,然后再根据TTFT、ITL、吞吐量和P99延迟判断系统是否达到了业务要求。对于长上下文、高并发的AI服务来说,KV Cache已经不是一个藏在Transformer内部的小型技术细节,而是决定单卡能够承载多少请求、系统需要多少GPU,以及推理成本最终落在哪里的重要工程变量。
KV Cache 为什么会占用大量 GPU 显存,它到底保存了什么数据
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP