大模型推理系统能够同时处理多少个请求,并不只是由GPU有多少TFLOPS决定。对于生成式LLM,随着并发请求增加,系统需要同时保存越来越多正在生成的序列,而每条序列都会产生自己的KV Cache。上下文越长、同时生成的请求越多,KV Cache占用的显存就越大,最终可能从一个性能优化机制变成限制并发数量的重要资源。
这也是为什么现代LLM推理框架越来越重视Paged KV Cache、Continuous Batching、Prefix Cache、KV Cache量化以及CPU或其他设备上的缓存管理。
理解这些技术之前,首先要把KV Cache究竟缓存什么、为什么需要缓存,以及它与并发之间是什么关系搞清楚。
KV Cache到底缓存了什么
Transformer的自注意力机制会产生Key和Value张量。模型处理一个序列时,过去Token对应的Key和Value在后续生成过程中仍然需要被访问。
如果每生成一个新Token都重新计算过去所有Token对应的Key和Value,就会产生大量重复工作。
因此,在自回归生成过程中,系统通常会把已经计算出来的Key和Value保存下来。后续生成新的Token时,可以直接复用已经存在的KV数据,同时计算新Token对应的Key和Value。
这就是KV Cache。
需要特别区分的是,KV Cache并不是把整个聊天记录以文本形式存进显存,也不是把模型所有隐藏状态完整保存下来。它主要保存注意力层后续计算需要使用的Key和Value张量。
这一区别非常重要,因为KV Cache的大小并不能简单根据模型参数量直接推算。
一个模型拥有多少参数,主要决定模型权重需要多少存储空间;KV Cache则与层数、KV头数量、每个头的维度、KV数据精度以及当前活跃Token数量密切相关。
Prefill和Decode决定了KV Cache的使用方式
LLM推理通常需要区分Prefill和Decode两个阶段。
用户提交Prompt之后,模型首先处理输入上下文,这一阶段通常称为Prefill。模型会对输入Token进行计算,并建立对应的KV Cache。
随后模型进入Decode阶段,一个Token接着一个Token地生成输出。
Decode阶段并不是每生成一个Token就重新计算整个历史上下文的Key和Value。已经计算过的KV可以继续使用,新生成的Token只需要把新增的Key和Value加入缓存。
因此,原稿中“每生成一个Token都需要进行O(n²)计算”的说法并不适合描述采用KV Cache的实际Decode过程。
注意力计算本身仍然会随着上下文长度增加而增加访问和计算工作,但KV Cache避免了对历史Token的Key和Value进行重复生成。
这也是KV Cache对LLM推理如此重要的原因。
KV Cache为什么会限制并发
假设GPU只有一个请求,那么系统只需要保存这一条请求的活跃上下文和生成状态。
当同时运行几十、几百甚至更多请求时,每一条请求都有自己的活跃Token以及对应KV Cache。
因此,并发增加以后,KV Cache通常也会快速增长。
可以用一个简化关系理解:
KV Cache容量大致取决于Transformer层数、KV头数量、每个KV头的维度、每个元素占用的字节数以及当前所有活跃序列的Token数量。
一个常见的近似表达方式是:
KV Cache ≈ Layers × 2 × KV Heads × Head Dimension × Tokens × Bytes per Element
这里的2代表Key和Value两部分。
这个公式只能作为估算框架,因为实际实现还会涉及内存布局、padding、block大小、量化方式以及运行时管理方式。
也因此,不能使用“每个Token固定占用16字节”或者“每个Token固定占用128字节”这样的数字去描述所有模型。
不同模型架构和精度配置之间可能存在很大的差异。
长上下文为什么特别吃显存
长上下文的问题非常直接。
如果一个请求只有几百个Token,那么它对应的KV Cache相对有限。
如果一个请求拥有几万甚至更长的上下文,那么需要保存的KV数据就会明显增加。
当多个长上下文请求同时存在时,KV Cache会快速消耗GPU显存。
这里有一个很重要的区别:模型权重通常是相对固定的,而KV Cache属于动态工作集。
模型加载以后,权重占用大致保持稳定;但请求数量、上下文长度以及生成过程不断变化,KV Cache则会动态增长和释放。
因此,一张GPU是否能够承载更多并发请求,不应该只看“模型权重能不能装进去”,还必须考虑剩余显存能够容纳多少活跃KV Cache。
这也是为什么一个模型明明可以成功加载到GPU,却可能在高并发或者长上下文压力测试中出现OOM。
Batch Size并不能单独代表并发能力
很多人看到推理系统中的Batch Size,就认为Batch越大,并发能力越强。
实际情况复杂得多。
现代LLM服务通常还需要考虑Concurrent Sequences、Active Tokens、最大Batch Tokens、最大上下文长度以及调度等待时间等因素。
例如两个系统都设置Batch Size为32,但一个系统中的32条请求平均只有500个Token,另一个系统中的32条请求平均拥有16000个Token,它们对显存和计算资源的压力完全不同。
因此,对于LLM推理,仅仅统计“同时处理32个请求”并不足以描述KV Cache压力。
Active Tokens往往是更加有价值的观察指标。
Continuous Batching为什么重要
传统静态Batch通常需要等待一批请求形成固定批次,然后一起计算。
LLM生成存在一个特殊问题:不同请求生成结束的时间不同。
有的请求生成几十个Token就结束,有的请求可能持续几千个Token。
如果始终等待整个Batch中的所有请求同步结束,GPU资源可能无法得到充分利用。
Continuous Batching的思路是让推理服务器动态管理活跃请求,在请求完成以后及时释放相关资源,同时让新的请求进入正在运行的批次。
这对KV Cache管理尤其重要。
因为KV Cache本质上是动态工作集。请求进入,缓存增加;请求持续生成,缓存继续增长;请求完成,缓存释放。
如果调度器能够根据Token预算和显存容量动态管理这些序列,就能够比简单固定Batch更加充分地利用GPU资源。
因此,现代推理系统往往同时考虑最大并发序列数和最大Token预算,而不是只设置一个Batch Size。
Paged KV Cache解决了什么问题
长上下文并发还有一个容易被忽略的问题,就是显存管理。
如果每条请求都要求一块连续的大型显存区域,那么随着请求不断进入和退出,显存可能产生碎片。
Paged KV Cache借鉴了虚拟内存和分页管理的一些思想,把KV Cache拆分成固定或者近似固定大小的Block,再通过管理结构把这些Block映射到不同序列。
这样做的核心价值之一,是改善KV Cache的动态内存管理和利用率。
它并没有改变KV Cache数据本身的基本大小。
也就是说,Paged KV Cache不是一种“压缩算法”,不能让一个Token本身需要的KV数据凭空消失。
它解决的是如何更高效地分配、回收和组织这些动态缓存。
GQA和MQA为什么也会影响并发
模型架构本身也会影响KV Cache规模。
传统Multi-Head Attention中,每个注意力头都有对应的Key和Value头。
而Grouped-Query Attention,也就是GQA,可以让多个Query头共享部分KV头。
Multi-Query Attention,也就是MQA,则进一步减少KV头数量。
由于KV Cache主要保存Key和Value,因此减少KV头数量通常可以明显降低每个Token对应的KV存储需求。
这也是为什么分析某个模型的KV Cache成本时,不能只看模型参数量。
两个参数量接近的模型,如果层数、KV头数量、Head Dimension或者KV精度不同,它们的KV Cache占用完全可能不同。
KV Cache和模型量化不是一回事
模型使用INT8、FP8或者其他量化方式,并不意味着KV Cache一定采用完全相同的精度。
模型权重、激活值以及KV Cache属于不同的数据对象,具体采用什么精度取决于模型、推理框架、GPU硬件和实现方式。
因此,“模型已经4-bit量化,所以KV Cache也只有4-bit”这种判断并不成立。
KV Cache量化确实可以成为降低显存占用的一种手段,但需要考虑精度损失、硬件支持、内核实现以及实际吞吐和延迟变化。
对于生产系统,不能只计算理论显存节省多少,还应该测量TTFT、ITL、吞吐量以及长上下文场景下的P95和P99延迟。
Prefix Cache与普通KV Cache需要分开理解
还有一个经常被混淆的概念是Prefix Cache。
普通KV Cache主要服务于正在执行的请求。
Prefix Cache则可以让不同请求复用已经计算过的相同前缀对应的KV Cache。
例如大量请求都包含相同的系统提示词、工具定义或者固定文档前缀,那么这些重复部分的Prefill计算就可能被复用。
但Prefix Cache通常需要足够精确的Token级前缀匹配。两个Prompt只是语义相似,并不意味着它们可以直接共享同一份Prefix KV。
因此,Prefix Cache解决的是重复前缀计算的问题,而普通KV Cache解决的是单个活跃请求在Decode过程中的历史Key和Value复用问题。
两者可以一起使用,但不能混为一谈。
长上下文并不意味着缓存命中率一定更低
原稿认为长上下文的KV Cache“命中率往往低于短上下文”,这个判断并没有普遍成立的依据。
普通KV Cache并不存在类似传统Web缓存那样简单的“命中率”概念。
在一个正在生成的请求中,已经存在的历史KV本来就是该请求Decode需要访问的缓存。
真正需要讨论“命中率”的场景,更接近Prefix Cache等可复用缓存。
Prefix Cache的效果取决于请求之间是否存在重复前缀、前缀有多长、请求模式是否稳定,以及缓存容量和淘汰策略。
因此,长上下文真正直接增加的压力首先是KV Cache容量、显存带宽和注意力访问成本,而不是一个简单的“长上下文命中率下降”。
KV Cache为什么也会影响Token生成速度
KV Cache不仅消耗显存容量,还会影响显存访问。
Decode阶段通常需要反复读取历史KV数据。
随着上下文长度增加,需要访问的数据规模也可能增加。对于某些工作负载,Decode性能因此会越来越受到显存容量和带宽的限制。
这也是为什么GPU理论TFLOPS很高,并不意味着LLM一定拥有对应比例的Token/s。
推理性能还取决于模型权重访问、KV Cache访问、注意力计算、Kernel效率、Batch和并发策略以及GPU内存系统。
在一些Decode工作负载中,增加Batch可以提升整体吞吐,因为GPU获得更多并行工作;但同时也可能增加单请求等待时间和尾延迟。
所以性能测试必须同时观察吞吐量和用户体验指标。
长上下文下为什么调度变得困难
长上下文请求的另一个问题是资源不均衡。
假设一个请求只需要处理1000个Token,另一个请求需要处理50000个Token。
如果两者都按照“一个请求占一个并发槽位”来管理,那么这个并发模型实际上忽略了它们对GPU内存和计算资源的巨大差异。
现代推理调度器因此通常需要更加关注Token数量、KV Cache占用以及请求生命周期。
系统可能需要设置最大上下文长度、最大活跃序列数、最大Batch Tokens、最大等待时间以及显存安全边界。
这些参数之间存在相互影响。
把最大并发数简单设置得越高,并不意味着系统吞吐量一定越高。
当KV Cache接近显存容量以后,系统可能开始出现缓存驱逐、请求排队、CPU Offload或者其他内存管理行为,延迟反而可能明显上升。
CPU Offload不是免费的显存扩容
当GPU显存不足时,可以考虑把部分KV Cache放到CPU内存或者其他存储层。
这样能够扩大系统能够管理的缓存容量,但代价是数据移动。
GPU HBM的带宽和延迟与CPU内存、PCIe连接以及其他存储介质完全不同。
如果一个Decode过程频繁需要把KV数据从较慢的存储层重新搬回GPU,那么节省下来的显存空间可能会被额外的数据传输延迟抵消。
因此,Offload应该被理解成容量与性能之间的权衡,而不是免费的显存升级。
尤其是NVMe不能简单当作HBM的替代品。它可以作为更远端的存储层,但不能等同于GPU显存中的活跃KV Cache。
多GPU环境更加复杂
单GPU情况下,KV Cache主要是本地显存管理问题。
到了多GPU和多节点环境,情况会更加复杂。
如果模型使用Tensor Parallel或者其他模型并行方式,不同GPU可能保存不同部分的模型计算状态和KV数据。
此时GPU之间需要进行相应的数据交换。
使用NVLink、NVSwitch或者PCIe,会产生不同的通信性能特征。
跨节点部署时,还可能涉及InfiniBand或者RoCE等高速网络。
但不能简单地说“KV Cache必须通过网络同步”,也不能认为所有分布式推理都需要一个统一的全局KV Cache。
具体通信方式取决于并行策略、推理框架和KV Cache布局。
对于很多场景,让请求尽可能保持在合适的GPU或节点上,减少无必要的数据搬运,反而可能比建立一个巨大的远程共享缓存更加有效。
并发能力最终取决于SLO
一个推理系统到底能够承载多少并发请求,没有一个适用于所有模型的固定数字。
需要同时考虑模型规模、量化方式、KV架构、GPU显存容量、显存带宽、上下文长度、输出长度、并发请求数量、Batch策略以及模型并行方式。
更重要的是业务SLO。
离线批处理可能更关注总吞吐量。
在线聊天可能更加关注TTFT,也就是用户提交请求以后多久看到第一个Token,以及ITL,也就是后续Token之间的生成间隔。
企业API服务则可能同时要求P50、P95和P99延迟保持在一定范围。
如果为了追求更高吞吐而不断增加并发,结果导致P99延迟急剧上升,那么从在线服务角度看,这种优化可能并不成功。
应该监控哪些指标
真正部署LLM推理服务时,不应该只监控GPU利用率。
至少应该同时观察活跃序列数量、Active Tokens、KV Cache使用量、KV Cache分配和释放情况、显存使用量、显存带宽、GPU计算利用率、TTFT、ITL、输出Token吞吐量、输入Token吞吐量以及P50、P95、P99延迟。
如果系统支持Paged KV Cache,还可以观察Block使用情况、碎片和缓存回收。
如果使用Prefix Cache,则应该进一步记录Prefix命中的Token数量、命中请求数量、Prefill节省时间以及缓存占用。
这些指标放在一起,才能判断问题究竟是显存容量不足、KV Cache访问压力、GPU计算不足、调度策略不合理,还是请求本身存在异常长的上下文。
KV Cache管理本质上是资源管理
KV Cache看起来像Transformer里的一个优化技巧,但到了生产环境,它实际上已经成为推理系统的核心资源管理问题。
模型权重决定了GPU需要长期保存多少固定数据,KV Cache决定了系统在当前工作负载下还需要多少动态工作空间。
上下文越长,同时运行的请求越多,动态工作集就越大。
这也是为什么现代推理框架需要Paged KV Cache、Continuous Batching、Prefix Cache以及各种调度策略。
这些技术解决的问题并不完全相同。
Paged KV Cache主要改善动态显存管理。
Continuous Batching负责更灵活地调度活跃请求。
Prefix Cache减少重复前缀的Prefill计算。
KV Cache量化可以降低部分缓存的存储成本。
CPU Offload则通过牺牲部分访问性能换取更大的缓存容量。
没有一种技术可以单独解决所有问题。
对于一个长上下文、高并发LLM服务,最有效的优化方式通常不是盲目增加Batch,也不是单纯更换一张显存更大的GPU,而是先测量每条请求的上下文长度、Active Tokens、KV Cache占用、Prefill和Decode时间、显存带宽以及延迟分布,再确定瓶颈在哪里。
当这些指标被放到同一个性能模型中以后,KV Cache就不再只是一个Transformer内部的技术名词,而成为理解大模型并发能力、显存利用率和长上下文成本的重要入口。
KV Cache 如何影响大模型并发能力,长上下文场景为什么特别依赖缓存管理
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP