← 返回资讯

模型需要多少显存:权重、KV cache 与余量的三块估算法

2026-08-07

「70B 的模型要多少显存?」这个问题我在群里见过至少一百个答案,从「两张卡够了」到「八张都紧张」都有,而且每个人都很笃定。它们不是有人在胡说,是这个问题本身就问得不完整——同一个模型,换一组前提,显存需求能差出一个数量级。

一个显存数字要有意义,至少得先说清四件事:

  1. 量化位宽——权重按几字节存;
  2. 上下文长度——单条请求最长多少 token;
  3. 并发数——同时要在飞的请求有几条;
  4. 推理框架留多少余量——框架自己要占一块,还得留出激活值和碎片。

这四个条件缺一个,答案就没法验证。所以下面不给你一张「什么模型配什么卡」的表——那种表我也做不出可信的——而是给方法:把显存拆成三块,你拿自己要跑的那个模型的配置,自己算。

第一块:模型权重——固定开销

这是最简单的一块,一句话就能说完:

权重显存 ≈ 参数量 × 每参数字节数

每参数字节数由权重的数据类型决定,这是数据类型的定义,不是谁的实测结论:

精度每参数字节数
FP324
FP16 / BF162
FP8 / INT81
INT40.5

拿一个假设的 70 亿参数模型演示(下面所有数字都是按这个假设参数量直接换算出来的理论值,不对应任何具体模型):

精度算式十进制 GB换算成 GiB
FP16 / BF167×10⁹ × 2 字节≈ 14 GB≈ 13.0 GiB
INT87×10⁹ × 1 字节≈ 7 GB≈ 6.5 GiB
INT47×10⁹ × 0.5 字节≈ 3.5 GB≈ 3.3 GiB

这里有个很多人栽过的小坑:显卡标称的「24GB」是十进制还是二进制?厂商标称通常按十进制 GB,而 nvidia-smi 报的是 MiB(二进制)。1 GB = 10⁹ 字节,1 GiB = 2³⁰ ≈ 1.074×10⁹ 字节,两者差约 7%。估算到接近卡容量上限的时候,这 7% 足够让「刚好装得下」变成「装不下」。所以上表我把两种口径都列了——你自己算的时候也养成这个习惯。

再强调两点:

  • 这只是权重本身,不含量化时的 scale/zero-point 等辅助张量、不含 embedding 之外的额外缓冲。实际加载完的占用会比这个数高一点。
  • 这一块是固定开销:跟你开多少并发、跑多长上下文完全无关。模型加载进去占多少,就一直占多少。

关于量化到底怎么选、各种量化格式差在哪,我在量化怎么选里单独写过。这里只说跟显存有关的部分:量化能把第一块按比例砍下来,但砍不动第二块。而第二块,才是真正会把你送进 OOM 的东西。

第二块:KV cache——这才是变量

自回归生成时,每生成一个 token 都要用到前面所有 token 的 Key 和 Value。为了不重算,框架把它们缓存下来,这就是 KV cache。它的规模是这么来的:

KV cache 字节数
  ≈ 2 × 层数 × 每层 KV 头数 × 每头维度 × 序列长度 × 并发数 × 每元素字节数

开头那个 2 是因为 K 和 V 各存一份,不是什么经验系数。

这些参数在哪查? 不用问别人,模型仓库根目录的 config.json 里就有。HuggingFace 的通用字段名是:

  • num_hidden_layers——层数
  • hidden_size——隐藏层维度
  • num_attention_heads——注意力头数
  • num_key_value_heads——KV 头数(用了 GQA/MQA 的模型才会小于上面那个)
  • torch_dtype——权重原始精度,用来定第一块的每参数字节数

每头维度一般是 hidden_size / num_attention_heads。KV cache 的每元素字节数由 KV cache 自身的精度决定,跟权重量化不是一回事——权重量化到 INT4,KV cache 仍然可能是 FP16 存的,除非你另外开了 KV cache 量化。这一点经常被搞混,导致「我都量化到 4bit 了怎么还是爆」。

拿一组完全假设的配置走一遍(再说一次:以下数字是我编的演示值,不对应任何真实模型,你必须换成自己 config.json 里的实际值):

假设 num_hidden_layers = 32hidden_size = 4096num_attention_heads = 32num_key_value_heads = 8、KV cache 用 FP16(2 字节)。

每头维度 = 4096 / 32 = 128。

单个 token 的 KV cache:

2 × 32 层 × 8 个 KV 头 × 128 维 × 2 字节 = 131072 字节 = 128 KiB / token

那么:

场景(假设配置下)算式KV cache
单请求,8K 上下文128 KiB × 81921 GiB
单请求,32K 上下文128 KiB × 327684 GiB
32 并发,各 8K 上下文1 GiB × 3232 GiB
64 并发,各 32K 上下文4 GiB × 64256 GiB

看最后一行。在这个假设配置下,KV cache 已经比权重(不管你量化到几 bit)大了一个数量级不止。这就是为什么只谈参数量不谈并发和上下文的显存答案没有意义。

三个必须记住的直觉:

一、KV cache 随上下文长度线性增长。max_model_len 从 8K 放到 32K,不是「稍微多占一点」,是每条在飞请求的 KV 占用直接翻两番。很多人上线时用短 prompt 测得好好的,一接入长文档 RAG 就炸,原因就在这。

二、KV cache 随并发数线性增长。 而且并发是最容易失控的维度——压测时你控制并发,线上流量不听你的。第三块的余量主要就是为这个准备的。

三、GQA/MQA 这类共享 KV 头的设计会显著压低它。 多个 Query 头共用一组 K/V 头,公式里 num_key_value_heads 那一项就小下来了,而 KV cache 总量跟这一项成正比。上面那个假设配置里 KV 头是 Query 头的四分之一;如果同样规模的模型用传统 MHA(num_key_value_heads 等于 num_attention_heads),同一条式子算出来就是 512 KiB/token,四倍。所以选模型时 num_key_value_heads 这个字段值得专门看一眼,它对你能开多大并发的影响,可能比参数量本身还直接。

第三块:余量——别把显存算到刚好够

前两块之外,还有一堆东西要吃显存:

  • 激活值:前向计算的中间结果,跟批大小和序列长度都相关;
  • 显存碎片:反复分配释放留下的空洞,PagedAttention 这类分页机制能缓解但消不掉;
  • 框架与运行时自身开销:CUDA context、通信缓冲区、编译产物、日志与指标采集等等;
  • 多卡时的通信缓冲:切分方案不同,这块也不同。

我不打算给你一个「留 X% 是标准做法」的数字——我没见过哪家官方文档把这个百分比定死过,凭印象编一个反而更害人。我的习惯是在前两块算出来的基础上留出明显的余量再去选卡,具体比例靠实测校准:第一次部署跑起来之后,用真实并发压一轮,看实际峰值占用比你估的高多少,把这个差值记下来,以后同类模型就用你自己的这个系数。

反过来说,千万不要把显存算到刚好够。算得刚好的典型结局是:单请求测试一切正常,一上并发立刻 OOM,或者报「not enough KV cache space」。因为你算的是稳态,而真实流量的峰值永远比稳态高。

用 vLLM 的参数把估算和现实对上

上面三块是纸面估算。好消息是,如果你用 vLLM 部署,估算里的两个变量在框架侧都有直接对应的旋钮——估不准的时候,你有东西可以拧

根据 vLLM 官方 optimization 文档,跟显存直接相关的参数是这些(参数名逐字来自官方文档,各自的默认值官方页面没有明确给出,以你当次使用的官方文档与版本为准):

参数官方说明要点对应到上面哪一块
gpu_memory_utilization提高它可以给 KV cache 更多空间决定框架总共能用多少显存
max_model_len限制单个序列的最大 token 数卡住第二块里的「序列长度」
max_num_seqs一批里的并发请求数上限;调小会减少并发请求数,从而需要更少的 KV cache 空间卡住第二块里的「并发数」
max_num_batched_tokens单批次最大 token 数,影响 prefill 与 decode 的平衡;官方提到大于 8192 有利于吞吐影响激活值与调度
tensor_parallel_size把模型权重切分到多张 GPU,使每张卡有更多显存留给 KV cache摊薄第一块
pipeline_parallel_size把模型层分布到多张 GPU,降低每张卡上模型权重所需显存摊薄第一块
kv_cache_memory直接指定 KV cache 大小,跳过显存 profiling 阶段直接锁死第二块

对照着看会发现一件很舒服的事:max_model_lenmax_num_seqs 正好就是 KV cache 公式里那两个乘数。所以「装不下怎么办」不是玄学——降上下文上限,或者降并发上限,KV cache 需求就按比例下来。你在第二块里算出的那张表,其实就是这两个参数的取值依据。

vLLM 官方给出的 OOM / 「not enough KV cache space」缓解顺序是:

  1. 提高 gpu_memory_utilization
  2. 降低 max_num_seqsmax_num_batched_tokens
  3. 提高 tensor_parallel_size(代价:同步开销);
  4. 提高 pipeline_parallel_size(代价:延迟)。

这个顺序本身是有逻辑的:先在单卡内部找空间(第 1 步是「让框架敢用更多显存」,第 2 步是「降低需求」),单卡真的挤不出来了才动多卡拓扑,而多卡是要付出通信同步或流水线延迟代价的。照着这个顺序调,比一上来就加卡省钱得多。更多 vLLM 部署侧的细节我写在vLLM 自建推理服务里。

多卡:两种切法对显存的影响不一样

上面第 3、4 步都是加卡,但两者不能混为一谈。

tensor_parallel_size(张量并行)把模型权重横向切分到多张卡上。每张卡只放一部分权重,于是第一块在每张卡上变小,腾出来的空间自然归 KV cache。代价是每一层计算都需要卡间同步——通信频繁,对卡间互联带宽敏感。

**pipeline_parallel_size(流水线并行)**按层把模型分布到多张卡上:前几层在卡 0,后几层在卡 1,以此类推。它同样降低每张卡上模型权重所需的显存,但通信只发生在层的边界上,频次低得多;代价是单条请求要串行穿过所有阶段,延迟增加。

估算时的差别在于:两种切法都能摊薄第一块,但张量并行下每张卡的 KV cache 也是切开的,而流水线并行下不同阶段的卡承担的层不同、KV 分布也不同——别想当然地用「总显存 ÷ 卡数」来估单卡占用,尤其是层数不能被卡数整除的时候,分布往往是不均匀的。真要做多卡容量规划,纸面估到数量级就行,剩下靠实测。

至于到底选什么卡,除了显存还要看算力和互联,我在怎么选推理显卡里展开过。

正确的工作流

把上面串起来,我认为唯一靠谱的流程是这样:

  1. 按公式估个数量级。 权重按参数量 × 位宽算,KV cache 按 config.json 里的真实字段 × 你打算支持的上下文长度和并发数算,加上明显的余量。目标只是选对卡的档位,不是算准到 GB。
  2. 选卡,实际部署跑起来。 先用保守的 max_model_lenmax_num_seqs 起步,能跑通比能跑满重要。
  3. 用真实并发和真实上下文长度压一次。 注意是「真实」——用你线上实际会遇到的 prompt 长度和输出长度,不是用「你好」测。RAG 场景尤其要用真实检索结果拼出来的长 prompt。
  4. 观察实际峰值,回头修正你的估算模型。 记下实测占用与估算值的比值,这个系数以后可以复用。
  5. 按实测值反推安全的参数上限,写进部署配置。max_model_lenmax_num_seqs 把峰值封住,宁可拒绝超限请求,也不要让服务 OOM 挂掉——前者只影响一条请求,后者影响所有在飞请求。

最后一句是本文最想让你记住的:估算是用来选卡的,不是用来承诺容量的。 我见过不少团队拿一个纸面估算值去跟业务方承诺「能扛多少并发」,然后在上线当天翻车。公式给你的是数量级和方向,容量承诺只能来自你自己环境里的实测。如果你还在纠结自建这条路值不值得走,自建还是调 API 那篇里算过另一笔账。

自查清单

  • 我说的显存需求,有没有同时写明量化位宽、上下文长度、并发数这三个前提?没写全的数字自己都不要信。
  • 权重那一块,我用的是参数量 × 每参数字节数,并且区分了 GB 与 GiB 两种口径?
  • KV cache 我是从自己模型的 config.json 里取的 num_hidden_layershidden_sizenum_attention_headsnum_key_value_heads,还是抄的别人的数字?
  • 我有没有把 KV cache 的精度和权重量化精度搞混?权重量化不会自动缩小 KV cache。
  • 我算的是不是「峰值」——即最长上下文 × 最大并发,而不是平均情况?
  • 前两块之外,我留出的余量是明显的余量,还是刚好够?
  • max_model_lenmax_num_seqs 有没有按估算结果显式设死,而不是放任默认?
  • 上线前是否用真实长度的 prompt 和真实并发压过一轮,并用实测值回头校准了估算?

相关阅读