Skip to content

推理资源账:一次生成需要多少显存、算力与带宽 ​

1. 场景:别只算权重 ​

上线一个模型之前,最常被问的三个问题是:这模型要几块卡?响应多快?QPS 能扛多少?回答它们需要同一件事——把一次生成拆成显存、算力、带宽三本账。最容易犯的错是只算权重:模型 70B、FP16 就是 140GB,以为两块 80GB 卡够了,结果 KV 缓存一涨、工作区一占,直接 OOM。

本文沿用知识库"从约束推导设计"的方法:先列清楚搬什么、搬多少、搬几次、经过哪里、谁必须等它,再对照硬件容量、带宽与算力做数量级估算。所有公式均可复算,文末附 Python 算账函数。

2. 第一本账:显存 ​

一次推理实例的显存占用 = 权重 + KV 缓存 + 激活/工作区,缺一不可。

权重(已加载并驻留):

W=N×bytes_per_paramW = N \times bytes\_per\_param

精度bytes/参数70B 模型
FP324280 GB
FP16 / BF162140 GB
INT8170 GB
INT4(GPTQ/AWQ)0.5~0.635~42 GB

KV 缓存:每个 token 的键值对大小为

KV_per_token=2×L×K×H×bytesKV\_per\_token = 2 \times L \times K \times H \times bytes

其中 L=层数、K=KV heads 数、H=每个 head 的维度。GQA 下 K 远小于 Q heads,是 KV 缓存大小的关键开关。示例(BF16,即 bytes=2):

模型LKHKV/token4K 上下文32K 上下文
7B(GQA)28412828 KB115 MB917 MB
70B(GQA)808128160 KB655 MB5.2 GB

激活/工作区:prefill 阶段的大激活张量与引擎工作区(vLLM 默认预留显存的 90% 给 KV,gpu_memory_utilization 可调)。这部分随引擎与 batch 变化,估算时按权重 + KV 后的余量计。

合计口径:显存需求 ≈ 权重 + KV(按最大并发上下文规划) + 余量(≥10%)。这就是 vLLM 部署文档里"为什么 70B 要 4 卡起步、KV 量化能省 20-50%"的出处。

3. 第二本账:算力 ​

生成分两个阶段,计算量完全不同:

  • prefill(预填充):处理整段输入,一次并行算完。每 token 约 2×N2 \times N FLOPs(N=参数量,乘 2 是乘加各一次)。
  • decode(解码):逐 token 生成,每步只算 1 个 token,同样约 2×N2 \times N FLOPs,但复用 prefill 已算好的 KV,所以单步算力需求小、带宽需求大(见下)。

一次请求(输入 S token,输出 T token)的总算力:

FLOPs≈2N⋅S+2N⋅TFLOPs \approx 2N \cdot S + 2N \cdot T

实例(70B,S=1024,T=512):2×70e9×1536≈2.15×10142 \times 70e9 \times 1536 \approx 2.15 \times 10^{14} FLOPs。对照一张卡 FP16 算力(如 H800 ≈ 989 TFLOPs BF16,扣除算子效率按 50% 估),prefill 吞吐可达数十~上百 tokens/ms 级,而 decode 的瓶颈通常不在算力而在带宽。

4. 第三本账:带宽 ​

decode 是典型的"带宽饥饿"阶段:每生成 1 个 token,都要把全部权重从显存读一遍(激活参数前向一次)。单卡带宽账:

B_needed=N×bytes_per_param(bytes/token)B\_needed = N \times bytes\_per\_param \quad (bytes/token)

模型FP16 权重读取/ tokenHBM 带宽 3TB/s 的理论 TPOT
7B14 GB约 4.7 ms/token
70B140 GB约 47 ms/token

这个"每 token 读全部权重"正是 TPOT 的下限来源,也是为什么单卡 decode 快不过物理带宽、为什么 MoE 只激活部分专家(见第 2 篇)、为什么小 batch 下算力闲置而大 batch 下带宽被摊薄(见第 3 篇)。

prefill 则相反:输入 token 并行计算,读权重次数少、计算密集,瓶颈通常是算力而非带宽。两阶段背道而驰,推理系统因此要分别优化——这就是 vLLM continuous batching 拆 prefill/decode 的物理动机。

5. 算账工具:Python 函数(可直接复算) ​

python
def kv_bytes(num_layers, kv_heads, head_dim, seq_len, bytes_per=2):
    return 2 * num_layers * kv_heads * head_dim * seq_len * bytes_per

def weights_bytes(num_params, bytes_per=2):
    return num_params * bytes_per

def flops_request(num_params, input_tokens, output_tokens):
    return 2 * num_params * (input_tokens + output_tokens)

def decode_tpot(num_params, bytes_per, hbm_bw):
    """带宽约束的每 token 时间下限(秒)"""
    return num_params * bytes_per / hbm_bw

# 例:70B,GQA(80 层/8 KV heads/128 dim),FP16,4K 上下文,HBM 3TB/s
N = 70e9
print(weights_bytes(N) / 1e9, "GB 权重")          # 140 GB
print(kv_bytes(80, 8, 128, 4096) / 1e6, "MB KV")  # 655 MB
print(decode_tpot(N, 2, 3e12) * 1e3, "ms/token")  # ~46.7 ms

6. 实战检查清单 ​

  • [ ] 显存账三件套(权重 + KV + 工作区)都算了,没有只算权重
  • [ ] KV 按最大并发上下文规划(不是平均长度),并确认 GQA 层配置
  • [ ] decode 瓶颈用带宽核对(TPOT ≥ 权重字节/ HBM 带宽),不要只看算力峰值
  • [ ] prefill/decode 分开估:prefill 看算力,decode 看带宽
  • [ ] 量化收益用公式复核:INT4 减的是权重账,KV 量化减的是缓存账,别混为一谈

下一篇《从模型架构到资源需求》把注意力、KV 复用与 MoE 稀疏激活的量化影响接上这组公式。

方法参考:数量级估算与"数据搬移五问"框架参考开源书稿《深入理解 AI Infra:量化分析与系统设计》(李博杰,Apache-2.0,bojieli.github.io/ai-infra-book);本文公式与算例为本站自行推导与整理。

AI-HPC 智算超算专家 · Contact: openaihpc@gmail.com