☰
24G显存跑四路32K上下文:KV Cache与模型权重显存占用深度解析
2026/10/31 4:54:48 网站建设 项目流程

1. 24 GiB 显存里到底塞了些什么

先把问题拆开看。一张 24 GiB 的卡,比如 RTX 3090、4090 或者 A10,显存不是被模型权重一个人吃掉的,而是被好几拨人分着用。很多人算显存只算权重,结果一跑起来就 OOM,就是因为漏掉了后面几拨。

显存的主要占用方大致有这么几块:

  • 模型权重:这是最直观的一块。FP16 精度下,每 10 亿参数大约占 2 GiB。一个 7B 模型 FP16 大概 14 GiB,一个 13B 模型 FP16 大概 26 GiB——注意,13B 的 FP16 权重已经超过 24 GiB 了,单卡根本放不下。
  • KV Cache:这是推理时动态增长的部分,也是标题里"四路 32K 上下文"的关键。它跟上下文长度、并发路数、层数、注意力头数都相关,后面单独展开。
  • 激活值与临时缓冲:前向传播过程中的中间张量,虽然比训练时小很多,但也不是零。
  • CUDA 上下文与框架开销:CUDA runtime、cuBLAS、cuDNN 这些库本身要占几百 MiB 到 1 GiB 不等,vLLM 这类框架还有自己的调度和内存池开销。
  • 显存碎片:反复分配释放之后留下的空洞,实际可用显存会比理论值少。

所以"权重装进了 24 GiB"这句话本身就有歧义。如果指的是权重刚好占满 24 GiB,那 KV Cache 一点空间都没有,四路 32K 根本无从谈起。如果指的是权重装进了一张 24 GiB 的卡、还剩一些余量,那就要算清楚剩多少、够不够四路 32K。

我实际踩过的坑是这样的:拿一张 4090 跑 Qwen2.5-7B-Instruct 的 FP16,权重约 15 GiB,vLLM 启动后nvidia-smi显示占用约 16.5 GiB,也就是说框架和 CUDA 上下文吃掉了 1.5 GiB 左右。剩下大约 7.5 GiB 给 KV Cache。这 7.5 GiB 能不能撑起四路 32K,就是本文要算的核心账。

提示:算显存账的时候,永远用nvidia-smi实测值,不要用理论值。理论值和实测值之间通常有 1 到 2 GiB 的差距,这个差距在 24 GiB 卡上足以决定生死。

2. KV Cache 的显存公式与四路 32K 的实算

2.1 KV Cache 到底怎么算

KV Cache 的显存占用公式,业界通用的是这个:

KV Cache 字节数 = 2 × 层数 × 序列长度 × 注意力头数 × 头维度 × 精度字节数 × 并发路数

其中那个 2 是因为 Key 和 Value 各存一份。注意力头数乘以头维度,等于隐藏维度(对于标准 MHA 而言)。所以公式可以简化成:

KV Cache 字节数 = 2 × 层数 × 序列长度 × 隐藏维度 × 精度字节数 × 并发路数

但这里有个关键点:现在主流模型大多用了 GQA(分组查询注意力),Key 和 Value 的头数比 Query 少很多。比如 Qwen2.5-7B 是 28 个 Query 头、4 个 KV 头,头维度 128。这时候 KV Cache 要按 KV 头数算,不是按 Query 头数算。用 GQA 能把 KV Cache 直接砍到原来的几分之一,这也是为什么现在长上下文能跑起来的重要原因。

2.2 拿 Qwen2.5-7B 实算一遍

Qwen2.5-7B 的关键参数:28 层,隐藏维度 3584,Query 头 28 个,KV 头 4 个,头维度 128。FP16 精度,每元素 2 字节。

单路 32K 上下文的 KV Cache:

2 × 28 层 × 32768 序列长度 × 4 KV头 × 128 头维度 × 2 字节 = 2 × 28 × 32768 × 512 × 2 = 1,879,048,192 字节 ≈ 1.75 GiB

四路就是 1.75 × 4 = 7 GiB。

再回头看显存账:权重 15 GiB + 框架开销 1.5 GiB + KV Cache 7 GiB = 23.5 GiB。刚好卡在 24 GiB 的边缘,几乎没有余量。实际跑起来,只要有一点碎片或者调度开销,就会 OOM。

2.3 不同模型的对比

把几个常见模型拉出来对比一下,四路 32K 的 KV Cache 需求差异很大:

模型层数KV 头数头维度单路 32K KV Cache四路合计
Qwen2.5-7B2841281.75 GiB7.0 GiB
Llama-3.1-8B3281284.0 GiB16.0 GiB
Qwen2.5-14B4881286.0 GiB24.0 GiB
Mistral-7B3281284.0 GiB16.0 GiB

这张表说明一个很反直觉的事实:同样是 7B 级别的模型,KV Cache 需求能差一倍以上。Qwen2.5-7B 因为 KV 头只有 4 个,KV Cache 特别省;Llama-3.1-8B 是 8 个 KV 头,直接翻倍。所以选模型的时候,GQA 的 KV 头数是个必须看的指标,它直接决定了你能开多少并发、多长上下文。

注意:上表算的是纯 KV Cache,没算权重。Llama-3.1-8B 的 FP16 权重约 16 GiB,加上 16 GiB 的 KV Cache,四路 32K 在 24 GiB 卡上根本不可能。Qwen2.5-14B 更不用说,权重就 28 GiB 了,单卡放不下。

3. 权重精度怎么选,直接决定 KV Cache 的生存空间

3.1 FP16、INT8、INT4 的取舍

权重精度是省显存最直接的手段。FP16 每参数 2 字节,INT8 是 1 字节,INT4 是 0.5 字节。一个 7B 模型:

  • FP16:约 14 GiB
  • INT8:约 7 GiB
  • INT4:约 3.5 GiB

从 FP16 换到 INT4,权重直接省出 10 GiB 以上,这 10 GiB 全部可以给 KV Cache。原本四路 32K 紧张的 Qwen2.5-7B,换 INT4 之后四路 32K 的 7 GiB KV Cache 绰绰有余,甚至能开到八路。

但量化是有代价的。INT4 量化会带来一定的精度损失,尤其是对数学推理、代码生成这类任务,损失比较明显。INT8 相对温和,精度损失通常在可接受范围内。我的经验是:

  • 对话、摘要、检索这类任务,INT4 完全够用。
  • 代码、数学、复杂推理,优先 INT8,实在不行再考虑 INT4。
  • 如果显存实在紧张,可以考虑 AWQ 或 GPTQ 这类带校准的量化方法,比朴素的 round-to-nearest 量化效果好不少。

3.2 vLLM 里的量化配置

vLLM 支持多种量化格式,启动时通过--quantization参数指定。以 AWQ 为例:

python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-7B-Instruct-AWQ \ --quantization awq \ --max-model-len 32768 \ --gpu-memory-utilization 0.95 \ --max-num-seqs 4

这里几个参数值得说清楚:

  • --max-model-len 32768:把最大上下文设成 32K。这个值设得越大,vLLM 预留的 KV Cache 块越多,但实际占用是按需增长的。
  • --gpu-memory-utilization 0.95:告诉 vLLM 可以用 95% 的显存。这个值默认是 0.9,设太高容易和系统抢显存,设太低浪费。0.95 是个比较激进的平衡点。
  • --max-num-seqs 4:限制并发序列数为 4,正好对应"四路"。这个参数直接控制 KV Cache 的上限。

3.3 一个容易忽略的细节:KV Cache 的精度

vLLM 支持把 KV Cache 也量化成 FP8,通过--kv-cache-dtype fp8开启。这能把 KV Cache 直接砍一半。对于四路 32K 的场景,Qwen2.5-7B 的 7 GiB KV Cache 能降到 3.5 GiB,省出来的 3.5 GiB 非常可观。

但 FP8 KV Cache 对精度有影响,尤其是长上下文场景下,注意力分数的误差会累积。我实测下来,短上下文(4K 以内)几乎无感,32K 这种长上下文,生成质量会有轻微下降,表现为偶尔的重复或细节丢失。如果任务对精度敏感,慎用;如果是检索、摘要这类容错性高的任务,可以放心开。

4. 四路 32K 到底装不装得下:分场景结论

4.1 场景一:Qwen2.5-7B FP16 + 四路 32K

权重 15 GiB,框架开销 1.5 GiB,KV Cache 7 GiB,合计 23.5 GiB。理论上刚好卡进 24 GiB,但实际几乎必然 OOM,因为:

  • 显存碎片会吃掉几百 MiB。
  • vLLM 的块管理器有预分配开销。
  • 系统本身可能占用一点显存(如果卡还接了显示器)。

结论:装不下,或者勉强装下但极不稳定。必须做取舍。

4.2 场景二:Qwen2.5-7B INT8 + 四路 32K

权重降到约 7.5 GiB,框架开销 1.5 GiB,KV Cache 7 GiB,合计 16 GiB。24 GiB 卡上还剩 8 GiB 余量,非常宽裕。甚至可以开到六路或八路。

结论:轻松装下,还有余量。这是我最推荐的配置,精度损失小,显存宽裕。

4.3 场景三:Qwen2.5-7B INT4 + 四路 32K

权重约 4 GiB,框架 1.5 GiB,KV Cache 7 GiB,合计 12.5 GiB。余量巨大,可以开到十路以上,或者把上下文拉到 128K。

结论:绰绰有余。适合对精度要求不高的批量任务。

4.4 场景四:Llama-3.1-8B 任意精度 + 四路 32K

Llama-3.1-8B 的 KV Cache 是 Qwen2.5-7B 的两倍多(16 GiB vs 7 GiB)。即使 INT4 量化,权重 4 GiB + 框架 1.5 GiB + KV Cache 16 GiB = 21.5 GiB,勉强能塞进 24 GiB,但没有任何余量,实际跑起来大概率 OOM。

结论:四路 32K 基本不可能,除非把上下文降到 16K 或并发降到 2 路。

把上面几个场景汇总成一张表:

配置权重框架KV Cache合计24 GiB 可行性
Qwen2.5-7B FP16151.5723.5极勉强,易 OOM
Qwen2.5-7B INT87.51.5716轻松
Qwen2.5-7B INT441.5712.5绰绰有余
Llama-3.1-8B INT441.51621.5勉强,无余量

5. 装不下的时候,有哪些腾挪空间

5.1 降上下文长度

32K 降到 16K,KV Cache 直接减半。Qwen2.5-7B FP16 的四路 16K 只要 3.5 GiB KV Cache,合计 20 GiB,稳稳装下。这是最直接的腾挪手段,代价是上下文变短。

如果你的任务其实用不到 32K,比如大部分对话在 4K 以内,那完全没必要开 32K。vLLM 的--max-model-len设成实际需要的长度就行,设大了纯属浪费预留空间。

5.2 降并发路数

四路降到两路,KV Cache 减半。如果并发需求没那么高,这是最省事的办法。但要注意,vLLM 的--max-num-seqs控制的是同时处理的序列数,实际请求是排队进来的。设成 2 意味着同时只有 2 个请求在跑,第 3 个要等。如果 QPS 要求高,这个不能随便降。

5.3 开启 FP8 KV Cache

前面提过,--kv-cache-dtype fp8能把 KV Cache 砍半。Qwen2.5-7B FP16 的四路 32K 从 7 GiB 降到 3.5 GiB,合计 20 GiB,装得下了。代价是长上下文精度轻微下降。

5.4 用张量并行分到多卡

如果手上有两张 24 GiB 卡,可以用张量并行(TP=2)把权重和 KV Cache 分摊到两张卡上。vLLM 启动时加--tensor-parallel-size 2。这样每张卡只承担一半,四路 32K 轻松装下。代价是需要两张卡,成本翻倍。

5.5 换更省 KV Cache 的模型

回到那张对比表,Qwen2.5-7B 的 KV 头只有 4 个,是同类里最省的之一。如果模型可以换,优先选 KV 头少的。这不是玄学,是实打实的显存账。

提示:选模型的时候,除了看参数量,一定要看num_key_value_heads这个参数。它比参数量更能决定你的并发和上下文能力。很多模型卡上不直接写这个值,要去 config.json 里翻。

6. 实测中踩过的坑和调参心得

6.1--gpu-memory-utilization不是越高越好

很多人为了榨干显存,把这个值设到 0.98 甚至 0.99。结果启动时看着正常,一跑长上下文就 OOM。原因是 vLLM 按这个比例预留显存,但实际运行时还有临时分配,预留太满就没有腾挪空间。我的经验是 0.90 到 0.95 之间最稳,0.95 是激进但可用的上限。

6.2 启动时的显存峰值比运行时高

vLLM 启动加载权重的时候,显存占用会有一个峰值,比稳定运行时高。如果启动能过、运行就 OOM,说明是运行时 KV Cache 增长导致的;如果启动就 OOM,说明权重加框架开销已经超了。这两种情况的处理方式完全不同,要先分清。

6.3 长上下文的首个请求特别慢

32K 上下文的第一个请求,vLLM 要预填充(prefill)整个 32K 的 KV Cache,这个阶段计算量大、耗时长。如果并发四路同时来,四个 prefill 挤在一起,显存和算力都会紧张。实际部署时,可以考虑限制单次 prefill 的 token 数,或者用 chunked prefill 把长 prefill 拆开。

6.4 监控显存要用对工具

nvidia-smi看的是整体占用,看不到 KV Cache 和权重的细分。vLLM 自己的日志里会打印 KV Cache 的块数和可用块数,这个更有参考价值。启动日志里那行GPU KV cache size: XXX tokens直接告诉你 KV Cache 能装多少 token,拿它除以 32768 就知道能开几路 32K。

6.5 别忽略 CUDA 版本和驱动

vLLM 对 CUDA 版本有要求,版本不匹配会导致各种奇怪的报错,有时候表现为显存占用异常。装之前先确认驱动和 CUDA 版本,别在这上面浪费时间。我遇到过驱动版本偏低导致 vLLM 无法识别全部显存的情况,升级驱动后就好了。

7. 一个可复现的配置模板

把上面的结论落成一个可以直接抄的配置。目标:24 GiB 单卡,四路 32K,Qwen2.5-7B,平衡精度和显存。

python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-7B-Instruct-AWQ \ --quantization awq \ --max-model-len 32768 \ --max-num-seqs 4 \ --gpu-memory-utilization 0.92 \ --kv-cache-dtype auto \ --port 8000

启动后看日志里的 KV Cache 行,确认可用 token 数大于 4 × 32768 = 131072。如果不够,按顺序尝试:先降--gpu-memory-utilization到 0.90,再开--kv-cache-dtype fp8,还不行就降--max-num-seqs到 3 或 2。

如果坚持要 FP16 精度,把模型换成非 AWQ 版本,同时把--max-num-seqs降到 2,或者把--max-model-len降到 16384。这两个取舍二选一,别指望 FP16 加四路 32K 能在 24 GiB 上稳定跑。

我个人在实际操作中的体会是,24 GiB 卡跑四路 32K,INT8 或 AWQ 量化几乎是必选项,FP16 留给短上下文或者低并发场景。显存账要提前算,别等 OOM 了再回头找原因,那时候已经浪费了一堆调试时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询