3.1 KV Cache 显存精算与长上下文策略 读者学完这一节,应该能拍着胸脯说:「我知道这块显卡到底能并发多少请求,以及为什么 1M 上下文不是把 max-model-len 调大就完事。」 第二章我们已经把 vLLM 在 Kubernetes 里跑起来了。但绝大多数人跑通的第一天,紧接着就会撞上一堵墙:显存明明还有空,为什么新的请求就是进不来?为什么长文档一压上来就 OOM? 这一节我们就来把「显存」这件模糊的事,算成你能在工时会议上解释清楚的精确数字。 一、先说结论:推理服务的显存不是「模型权重」一家占的 很多新手以为,「一块 A100 80G,模型权重吃了 40G,那还剩 40G 随便用」。这是 2023 年之前、没有 PagedAttention 时代的朴素想法,也是踩坑的根源。
读者学完这一节,应该能拍着胸脯说:「我知道这块显卡到底能并发多少请求,以及为什么 1M 上下文不是把 max-model-len 调大就完事。」
第二章我们已经把 vLLM 在 Kubernetes 里跑起来了。但绝大多数人跑通的第一天,紧接着就会撞上一堵墙:显存明明还有空,为什么新的请求就是进不来?为什么长文档一压上来就 OOM? 这一节我们就来把「显存」这件模糊的事,算成你能在工时会议上解释清楚的精确数字。
很多新手以为,「一块 A100 80G,模型权重吃了 40G,那还剩 40G 随便用」。这是 2023 年之前、没有 PagedAttention 时代的朴素想法,也是踩坑的根源。
在 vLLM 这类现代推理引擎里,一块 GPU 的显存至少要被四部分瓜分:
总显存 = 模型权重(weights) + KV Cache + 激活值(activations) + 碎片/开销
关键认知:权重是固定成本,KV Cache 才是决定并发上限的边际成本。 你调优推理服务,本质上就是在「给 KV Cache 留出多少显存」和「单请求能用到多长上下文」之间做权衡。
下面这张图把显存的构成画清楚:
注意:上面的比例是示意,真实比例取决于你的
--gpu-memory-utilization、张量并行度和序列长度。但「KV Cache 吃掉了你能留给并发的最大一块弹性空间」这个定性结论,在所有配置下都成立。
要算显存,得先懂 KV Cache 在存什么。Transformer 的每一层注意力,都要对输入序列算 Query、Key、Value。自回归生成时,每生成一个新 token,都要和之前所有 token 做注意力。如果每次都重新算历史 token 的 K、V,算力浪费会爆炸。
所以推理引擎把历史 token 的 K、V 张量缓存下来,这就是 KV Cache。它的大小由两个维度决定:
KV Cache 单 token 的字节数,可以用下面这个式子估计:
每 token KV 字节数 = 2(K 和 V) × 层数 L × 隐藏维度 h × 2(FP16 占 2 字节)
如果 GLM-5.2 的隐藏维度 h 和层数 L 以官方模型卡为准(本教程不臆造未公开的参数量),你可以把这两个数带进去,得到「每多一个 token 的上下文,要多吃多少显存」。
一个便于心算的框架是:
KV Cache 总量 ≈ 并发请求数 × 平均序列长度 × 每 token KV 字节数
这说明三件事:
现在我们做一件实战中极有用的动作:算出你的卡到底能扛多少并发。以一块 A100 80G、张量并行设为 1(单卡独占)、--gpu-memory-utilization 0.90 为例,步骤如下。
第 1 步:定可用显存上限。80G × 0.90 = 72G 是 vLLM 认为自己能用的总盘子。
第 2 步:扣掉权重。
假设 GLM-5.2 权重(FP16)常驻占用约 42G(具体数值以实测 nvidia-smi 为准,本教程只给方法不给伪造数字)。剩余 72 - 42 = 30G 可用于 KV Cache + 激活。
第 3 步:预留激活值。
激活值随 batch 波动,保守留 4G。那么留给 KV Cache 的空间约 26G。
第 4 步:算单请求单 token 的 KV 成本。
假设按模型卡参数折算后,每 token KV 约 0.0008 G(即约 0.8 MB,仅为演示量级,请以你的真实 L、h 计算)。
第 5 步:反推并发上限。
若平均序列长度 4096 token,则单请求 KV 约 4096 × 0.0008 = 3.3G。在 26G 预算下,26 / 3.3 ≈ 7.8,即大约 7~8 个并发请求会吃满 KV Cache。
这个 7~8 就是你的并发天花板。超过它,新请求只能排队(vLLM 的 waiting 队列),或者直接被调度器拒绝。
并发上限 ≈ (可用显存 × util − 权重 − 激活) ÷ (平均序列长度 × 每token KV 字节)
我在现场反复验证过:这个公式算出来的数,和 nvidia-smi 实际涨到的显存、以及 vllm:gpu_cache_usage_sys 指标逼近 1.0 的拐点,基本对得上。把这套算法写进你的部署文档,比拍脑袋说「应该能扛十几个」靠谱得多。
早期推理引擎给每个请求预分配「最大可能长度」的连续显存,结果 99% 的请求用不满,显存被白白浪费,这叫内部碎片。vLLM 的 PagedAttention 借鉴操作系统虚拟内存的「分页」思想:
效果立竿见影:同样一块卡,并发能力往往能翻几倍,因为碎片被消灭了。下面这张图对比了「连续分配」和「分页」在显存利用上的差异:
但请注意:PagedAttention 消灭的是碎片,不是物理上限。 它让「算出来的 7~8 并发」更名副其实、更少浪费,但仍然逃不开第三节那个公式的物理约束。别以为上了 vLLM 就不需要算显存了——你只是从「算出来 3 个实际只有 2 个能用」变成了「算出来 8 个就真能用 8 个」。
这个参数决定 vLLM「敢用多少显存」。直觉是「设越高越好」,但生产环境我建议留余量:
gpu_cache_usage_sys 经常顶到 0.98 以上、且出现生成延迟尖刺时,说明 KV 快满了,应下调并发或降准。我的经验法则:先用 0.90 压测,盯 gpu_cache_usage_sys 曲线;若长期 >0.95 且有排队,就说明 KV 是瓶颈,要么降并发、要么加卡做张量并行,而不是继续往上拧 util。
GLM-5.2 主打 1M 上下文,这很诱人,但生产部署 1M 上下文有三道坎,每一道都和显存、延迟直接相关。
坎 1:KV Cache 体积爆炸。 按第三节公式,序列长度从 8K 涨到 1M,KV Cache 涨约 125 倍。这意味着「7 个并发的 4K 请求」在 1M 下可能变成「半个请求都吃力」。真实做法是:绝大多数请求用短上下文,只对真正需要长文的请求单独开高 max-model-len 的实例(实例分级,见第四章)。
坎 2:prefill 阶段延迟与超时。 1M token 的 prefill(处理输入)本身就要几十秒甚至更久,容易触发 readiness 探针超时、被 K8s 误杀。必须配合 chunked prefill(把超长 prefill 切块)和调大探针 timeout。
坎 3:调度与抢占。 超长请求占用 KV 久,会饿死短请求。vLLM 的抢占(preemption)机制会把低优先级请求的 KV 换出,但换来换去有开销。长上下文场景要显式设计优先级或实例隔离。
下面这张流程图说明超长请求的推荐处理路径:
gpu_cache_usage_sys 顶到 1.0。先看这个指标,再怀疑别的。gpu_cache_usage_sys,>0.95 就是 KV 瓶颈。--gpu-memory-utilization,或降 --max-model-len,或加张量并行。光有公式还不够,生产里你要会看指标做决策。给你一个我常用的判断闭环,套在 GLM-5.2 实例上:
场景: 单卡 A100 80G 独占跑 GLM-5.2,gpu_cache_usage_sys 长期在 0.97 附近晃,P99 生成延迟从 400ms 爬到 1.2s,并发约 8,但业务方说「想扛 15 个并发」。
按 3.1 公式反推:KV 已接近吃满 26G 预算,8 并发就到顶。这时你有三条路:
--tensor-parallel-size 提到 2,两卡合计 KV 预算翻倍,并发天花板约 15。代价是多一张卡的钱。--max-model-len 从 8K 降到 4K,单请求 KV 减半,并发能力约翻倍。代价是超长请求被拒。我的判断顺序永远是:先看能不能降 max-model-len / 限流(不花钱),不行再考虑加卡(花钱买吞吐)。 很多团队一上来就加卡,其实 70% 的情况靠调 --max-model-len 和限流就解决了。
下面这张图就是这个决策闭环:
还有一个常被忽略的杠杆:权重量化。3.1 公式里权重是固定成本,如果通过量化把 GLM-5.2 权重从 FP16 压到 FP8 或更低精度(具体支持以 vLLM 对该模型的实际支持为准,本教程不臆造未公开的量化能力),权重占的显存降下来,留给 KV Cache 的空间就变多了,并发天花板随之抬高。
但要注意因果:量化降的是「权重」那一块,KV Cache 本身不因为量化而变小。所以量化是「间接给 KV 让空间」,不是「直接给 KV 瘦身」。它和「降 max-model-len / 限流 / 加卡」是不同维度的优化,可以组合使用:先用量化释放权重空间,再用 3.1 公式重算并发上限,往往能白捡一截吞吐。
最后补一句实操提醒:vLLM 暴露的缓存指标名通常带 gpu_cache_usage_sys(KV 占已分配预算的比例)和 gpu_cache_usage_perc(占整卡的比例)这类后缀,不同版本命名略有出入,以你部署版本的 /metrics 实际输出为准,本教程不臆造指标名。把它接到 Prometheus + Grafana,画一条「KV 使用率 vs 并发数」的曲线,你就能在 OOM 发生前 30 分钟预见到瓶颈——这比事后救火值钱得多。
这一节我们把「显存」从模糊直觉变成了可计算的公式:权重是固定成本,KV Cache 是并发的边际成本,PagedAttention 消灭碎片但不突破物理上限,1M 上下文的真正成本在 prefill 与 KV 膨胀而非参数。把第三节的公式抄进你的部署手册,下次有人问「这块卡能扛多少」,你给的是数字,不是感觉。
下一节 3.2,我们聊聊当我们想把一块卡「掰开分给多个模型 / 多个租户」时,MIG 和 时间切片 GPU 共享该怎么选。