3 minute read

部署大模型时,“模型文件能放进显卡”只是第一道门槛。真正运行起来后,显存还要容纳 KV Cache、临时激活、Logits、CUDA 工作区和多卡通信缓冲。输入越长、输出越长、并发越高,资源结构也会发生变化。

本文建立一套可复用的估算方法,用于回答四个问题:

  1. 一个模型的权重需要多少显存?
  2. 指定上下文和并发后,KV Cache 需要多少显存?
  3. 一张或多张 GPU 最多能部署多大的模型?
  4. 磁盘、CPU 内存和 GPU 显存在推理中分别做什么?

关于 Token、Attention、GQA、Prefill 和 Decode 的完整数据流,可以先阅读上一篇文章:大模型推理解析:从 Token、QKV 到 GQA 与 KV Cache

一、推理资源的总体构成

大模型推理涉及三个主要存储层级:

磁盘/SSD
  保存模型文件、Tokenizer、配置和编译缓存
        ↓ 启动时读取
CPU内存
  加载中转、请求调度、可选权重/KV卸载
        ↓ 复制或映射
GPU显存/HBM
  模型权重、KV Cache、临时激活和计算工作区

推理显存可以近似写成:

M_total
≈ M_weights
+ M_kv_cache
+ M_activations
+ M_logits
+ M_runtime

实际部署时不要把显存规划到 100%。通常先取:

M_usable = GPU总显存 × 0.85~0.90

剩余空间用于 CUDA Context、显存碎片、通信缓冲和瞬时峰值。

二、模型权重占用

权重占用的基础公式:

[ M_{\text{weights}} \approx P_{\text{total}}\times b_{\text{weight}} ]

其中:

  • (P_{\text{total}}):模型总参数量;
  • (b_{\text{weight}}):每个参数占用的字节数。
权重格式 理论位数 理论字节/参数 7B权重 32B权重 70B权重
FP32 32 bit 4 28GB 128GB 280GB
BF16/FP16 16 bit 2 14GB 64GB 140GB
FP8/INT8 8 bit 1 7GB 32GB 70GB
INT4 4 bit 0.5 3.5GB 16GB 35GB

这里的 GB 是十进制估算。显卡和操作系统有时使用 GiB:

1GB  = 10^9字节
1GiB = 2^30字节

所以 32B BF16 的 64GB 十进制权重,约等于 59.6GiB。

量化元数据

INT4/INT8 通常还需要:

  • 每组 Scale;
  • 可选 Zero Point;
  • 分组索引;
  • 未量化层;
  • 对齐和 Kernel Workspace。

因此不能简单认为 70B INT4 一定只占 35GB。实际可能是:

理论权重:35GB
实际权重:约38~45GB

最可靠的方法是查看所有权重分片的实际总大小,并在目标推理框架中加载后观察峰值。

Dense 与 MoE

Dense 模型中:

总参数量 ≈ 每个Token参与计算的参数量

MoE 模型中需要区分:

总参数量:决定权重显存
激活参数量:主要决定每个Token的计算量

例如一个“80B 总参数、3B 激活参数”的 MoE,权重显存仍接近 80B 模型,但每个 Token 不会经过全部专家。

三、KV Cache 占用

自回归生成时,每一层都要保存所有历史 Token 的 K 和 V:

[ M_{\text{KV}} = 2\times L\times B\times S \times n_{\text{kv}}\times d_{\text{head}} \times b_{\text{kv}} ]

变量含义:

变量 含义
(L) Transformer 层数
(B) 同时驻留的序列数
(S) 输入 Token 与已生成 Token 的总长度
(n_{\text{kv}}) KV Head 数量
(d_{\text{head}}) 每个 Head 的维度
(b_{\text{kv}}) KV 数据类型的字节数
2 K 和 V 两份

一个具体例子

假设:

L            = 32
n_kv         = 8
d_head       = 128
KV精度       = BF16,2字节
B            = 1

每新增一个 Token:

2 × 32 × 8 × 128 × 2
= 131072字节
≈ 128KiB

因此单请求约为:

1K Token   ≈ 128MiB
8K Token   ≈ 1GiB
32K Token  ≈ 4GiB
128K Token ≈ 16GiB

如果同时有 16 个 32K 上下文请求:

4GiB × 16 ≈ 64GiB

这还没有包含模型权重。

GQA 为什么能显著节省显存

假设 Q Head 数量为 32:

MHA:KV Heads = 32
GQA:KV Heads = 8

其他配置相同时,GQA 的 KV Cache 约为 MHA 的四分之一。

并发服务中的真实计算

使用 Paged Attention 时,缓存通常按固定 Token Block 分配。总 KV Cache 更接近:

[ M_{\text{KV,total}} \approx \sum_i M_{\text{KV}}(S_i) ]

而不是粗暴地让每个请求都占满统一最大长度。仍需考虑最后一个 Block 的未使用空间和显存碎片。

四、激活、Attention 与 Logits

隐藏状态

一份基础隐藏张量:

[ M_H=B\times S_{\text{new}}\times H\times b ]

例如:

B = 1
S_new = 32768
H = 4096
BF16 = 2字节

一份隐藏状态约为:

1 × 32768 × 4096 × 2
= 256MiB

单层内部还会产生 Q/K/V、Attention 输出、RMSNorm 缓冲和 MLP 中间张量,所以峰值会是基础隐藏状态的若干倍。

Prefill 与 Decode 的区别

Prefill:

hidden [B,S,4096]

一次并行处理完整提示词,激活和计算量较大。

Decode:

hidden [B,1,4096]

当前激活很小,但每一层都要读取越来越长的 KV Cache。

Attention 矩阵

朴素 Attention 会产生:

[B,Heads,S,S]

长上下文下可能极其庞大。FlashAttention 使用分块计算,避免把完整 (S\times S) 矩阵长期写入显存,但 Attention 的计算关系仍然存在。

Logits

完整 Logits 形状:

[B,S,Vocab]

例如:

S = 32768
Vocab = 150000
BF16 = 2字节

若保留所有位置:

32768 × 150000 × 2
≈ 9.2GiB

推理只需要最后位置的下一 Token 分布。高效引擎会尽量只生成或保留必要位置的 Logits。直接调用普通模型 forward() 时,需要检查它是否返回了完整序列 Logits。

五、运行时预留

除了模型张量,还需要:

  • CUDA Context;
  • cuBLAS/FlashAttention Workspace;
  • CUDA Graph;
  • 显存分配器碎片;
  • NCCL 通信缓冲;
  • 量化反量化缓冲;
  • 推理引擎预分配的 KV Block。

经验上可先预留:

小模型:2~4GiB
大模型:显存的5%~15%
多卡服务:额外考虑通信缓冲

最终必须使用真实模型、上下文和并发压测。

六、CPU内存和磁盘

磁盘

磁盘需要保存:

  • 模型分片;
  • Tokenizer 与配置;
  • 多种量化版本;
  • 下载和转换缓存;
  • 推理 Kernel 编译缓存。

建议:

可用磁盘 ≥ 目标模型文件的1.5~2倍

如果同时保存 BF16、INT8、INT4,三种文件大小需要相加。

CPU内存

模型加载可能经过:

SSD → CPU内存 → GPU显存

CPU 内存可能用于:

  • 权重加载中转;
  • Safetensors 内存映射;
  • 请求队列和 Token ID;
  • CPU Offload;
  • CPU KV Cache;
  • 多进程模型副本。

保守估算:

纯GPU部署:模型文件的1.2~1.5倍
需要转换/量化:模型文件的1.5~2.5倍
使用Offload:再加被卸载的权重和KV

流式加载和内存映射可以降低峰值,但不要默认 CPU 内存完全不需要模型空间。

七、不同推理阶段使用什么

阶段 主要使用的资源 典型瓶颈
模型加载 SSD、CPU内存、PCIe、GPU显存 文件读取和数据传输
Tokenizer CPU内存和CPU计算 CPU
Prefill 权重、激活、初始KV GPU算力
Decode 权重、持续增长的KV GPU显存带宽
Sampling 当前Logits 通常较小
请求结束 回收该请求KV块 内存管理

模型权重一般在服务运行期间常驻显存。请求生成 <EOS> 后,该请求的 KV Cache 被释放或归还缓存池。

八、判断一张卡能部署多大模型

核心约束:

[ M_{\text{weights}} +M_{\text{KV}} +M_{\text{runtime}} \leq M_{\text{usable}} ]

对于 80GB 显卡,若使用 90% 作为规划上限:

M_usable ≈ 72GB

32B BF16

理论权重:64GB十进制,约59.6GiB
量化/格式/运行时开销:额外若干GiB

可能可以单卡加载,但能留给长上下文和高并发 KV Cache 的空间有限。

70B BF16

理论权重:140GB

单张 80GB 卡无法部署,需要多卡切分或降低精度。

70B INT4

理论权重:35GB
实际可能:约38~45GB

一张 80GB 卡通常还能为 KV Cache 和运行时留下较多空间,但前提是模型格式、显卡和推理 Kernel 相互兼容。

一个实用原则

部署前必须同时确定:

最大输入Token
+ 最大输出Token
= 最大上下文长度

不能只根据输入长度计算 KV Cache,因为输出也会逐 Token 扩展缓存。

九、多卡部署

Tensor Parallel

理论上:

[ 每卡权重\approx\frac{总权重}{TP} ]

但每卡还需要自己的 KV Cache 分片、通信缓冲和未完全切分的模块。某些 GQA 模型在 TP 数大于 KV Head 数时还可能出现 KV 复制。

Pipeline Parallel

不同层放到不同 GPU:

GPU0:前半层
GPU1:后半层

每卡保存自己层的权重和 KV Cache,但需要跨卡传递隐藏状态。

Data Parallel

每张 GPU 都保存一份完整模型:

GPU0服务请求组A
GPU1服务请求组B

它提高服务总吞吐,但不降低单卡权重需求。

十、可运行的估算代码

下面的代码按 GiB 粗略估算权重与 KV Cache:

from dataclasses import dataclass

GIB = 1024 ** 3


@dataclass
class Estimate:
    weights_gib: float
    kv_cache_gib: float
    runtime_gib: float
    total_gib: float


def estimate_inference_memory(
    params_billion: float,
    weight_bits: int,
    num_layers: int,
    num_kv_heads: int,
    head_dim: int,
    max_context: int,
    concurrency: int = 1,
    kv_bits: int = 16,
    weight_overhead: float = 1.05,
    runtime_gib: float = 4.0,
) -> Estimate:
    params = params_billion * 1_000_000_000

    raw_weight_bytes = params * weight_bits / 8
    weight_bytes = raw_weight_bytes * weight_overhead

    kv_bytes = (
        2
        * num_layers
        * concurrency
        * max_context
        * num_kv_heads
        * head_dim
        * kv_bits
        / 8
    )

    weights_gib = weight_bytes / GIB
    kv_cache_gib = kv_bytes / GIB
    total_gib = weights_gib + kv_cache_gib + runtime_gib

    return Estimate(
        weights_gib=weights_gib,
        kv_cache_gib=kv_cache_gib,
        runtime_gib=runtime_gib,
        total_gib=total_gib,
    )


result = estimate_inference_memory(
    params_billion=32,
    weight_bits=16,
    num_layers=64,
    num_kv_heads=8,
    head_dim=128,
    max_context=32768,
    concurrency=1,
)

print(result)

这只是第一轮筛选。实际值还受到滑动窗口、部分层 Attention、KV 精度、量化格式、TP 分片方式和推理引擎实现影响。

十一、部署检查清单

部署前按顺序确认:

  1. 模型总参数量和权重文件实际大小;
  2. 权重精度及量化元数据;
  3. 层数、KV Head 数和 Head Dimension;
  4. 最大输入与最大输出 Token;
  5. 最大并发和 Batch 策略;
  6. KV Cache 精度;
  7. 单卡或多卡切分方式;
  8. 推理框架显存预留;
  9. 实测峰值显存、TTFT 和 Decode TPS;
  10. 对真实业务输入做并发压测。

总结

大模型推理显存不是只看“参数量 × 精度”。完整关系是:

权重决定模型能否加载
KV Cache决定长上下文和并发能力
激活决定Prefill峰值
工作区决定运行余量
磁盘和CPU内存决定加载与Offload能力

最实用的判断公式仍然是:

权重 + 目标上下文/并发的KV Cache + 运行余量
< 可规划GPU显存

先用公式筛选,再用真实模型和真实请求压测,才能得到可信的部署容量。