深蓝黑背景封面。左侧是一根横向堆叠条形图,标注「一次推理服务的显存总账」,从左到右四段:紫色长段「模型参数」注「权重本身,随参数量线性」;青色长段「KV Cache」注「随并发数 × 上下文长度增长」;灰色中段「激活与临时缓冲」注「随 batch 与算子实现波动」;深灰短段「框架与碎片开销」注「运行时预留与显存碎片」。条形图上方三条引线分别接到三个手段标签:量化指向参数段、PagedAttention 指向 KV Cache 段、连续批处理指向激活与开销段。右侧大标题「大模型推理的显存账本:参数、KV Cache、激活各占多少」,副标题「先算清每本账,再决定砍哪本」。左上标注「知识延伸 · 推理显存」,右上圆角胶囊标注「Day 02 · 11-19」。 图 1:本文封面,一次推理服务的显存总账与三把「省钱的刀」。

上一篇真题解析讲了 PagedAttention 的分页机制本身,本篇把镜头拉远:一次大模型推理服务,显存到底花在哪几本账上,每本账随什么增长、怎么估算、用什么手段省。面试里被问「7B 模型部署要多少显存」时,能当场把账算清的人,和只会说「参数乘 2 字节」的人,高下立判。先说明,本文为知识延伸文章,机制与公式以官方文档口径为准。

账一:模型参数,随参数量线性。

最直观的一本账。FP16 精度下每个参数 2 字节,N 十亿参数的模型就是约 2N GB:7B 约 13 GB,13B 约 24 GB,70B 约 130 GB。这是显存的地板,任何优化都绕不开它。对应的省法是权重量化:Hugging Face 量化文档列出的主流方案(GPTQ、AWQ、bitsandbytes 等)可以把权重压到 INT8(每参数 1 字节,减半)甚至 INT4(再减半),精度损失通常在可接受范围。注意量化只砍权重这本账,对 KV Cache 是另一套机制。

账二:KV Cache,随并发乘上下文长度增长。

这是推理服务真正的变量大头,公式要背下来:

KV Cache 字节数 = 2 × 层数 × KV 头数 × 头维 × 每元素字节 × token 总数

开头的 2 是 Key 和 Value 两路。以 7B 档典型配置(32 层、32 个 KV 头、头维 128、FP16)算,每个 token 的 KV Cache 约 512 KB——听起来不大,乘上规模就吓人:单条 2048 长度的序列约 1 GB,16 条并发就是 16 GB,64 条 4K 上下文直接破 128 GB,超过参数本身好几倍。省这本账的手段有三个层次:PagedAttention 消灭管理浪费(上一篇展开过,此处不重复);GQA/MQA 减少 KV 头数从源头缩小体积;KV Cache 量化把每元素字节再砍半或更多;最朴素的一条是控制最大上下文长度。

账三:激活与临时缓冲,随 batch 波动。

前向计算中的中间张量、算子临时缓冲。推理只有前向没有反向,激活比训练时小得多,但 batch 一大、算子没融合,峰值也不可小觑。省法是连续批处理(把更多请求摊进同一次计算,摊薄每条请求的固定开销)和算子融合(减少中间张量落盘)。

账四:框架与碎片开销,按零头预留。

运行时上下文、显存分配器的碎片,通常按总量的零头预留几 GB。PagedAttention 的固定块分配、显存池化本质上也在压这本账。

用代码把账本跑一遍。

下面这个估算器按上面两本主账的公式,对 7B 档模型算出各档数字(Node.js 22.15.0,node vram.js):

const GB = 1024 ** 3;
const params = 7e9, layers = 32, kvHeads = 32, headDim = 128;
console.log('参数显存(FP16):', (params * 2 / GB).toFixed(1), 'GB');
console.log('参数显存(INT8):', (params * 1 / GB).toFixed(1), 'GB');
console.log('参数显存(INT4):', (params * 0.5 / GB).toFixed(1), 'GB');

// KV Cache = 2(K,V) × 层数 × KV头数 × 头维 × 字节 × token数
const kvPerToken = 2 * layers * kvHeads * headDim * 2;
console.log('每 token 的 KV Cache:', (kvPerToken / 1024).toFixed(0), 'KB');
for (const [seqs, len] of [[1, 2048], [16, 2048], [64, 4096]]) {
  console.log(`并发${seqs}条×长度${len}:`, (kvPerToken * seqs * len / GB).toFixed(1), 'GB');
}
const budget = 80 * GB - params * 2 - 4 * GB; // 80G 卡扣掉参数与框架零头
console.log('80GB 卡 4K 上下文可装并发:', Math.floor(budget / (kvPerToken * 4096)));

实测输出:

参数显存(FP16): 13.0 GB
参数显存(INT8): 6.5 GB
参数显存(INT4): 3.3 GB
每 token 的 KV Cache: 512 KB
并发1条×长度2048: 1.0 GB
并发16条×长度2048: 16.0 GB
并发64条×长度4096: 128.0 GB
80GB 卡 4K 上下文可装并发: 31

一张 80 GB 的卡,7B 模型 FP16 占 13 GB,剩下预算按 4K 上下文只能装约 31 条并发——KV Cache 才是吞吐的真正天花板,这就是所有推理优化都围着它转的原因。

深蓝黑背景概念图。主体是一张四行三列的对照矩阵:行依次为「模型参数」「KV Cache」「激活与临时缓冲」「框架与碎片开销」;三列表头为「随什么增长」「怎么估算」「用什么手段省」。参数行:随参数量线性、每 10 亿参数 FP16 约 2 GB、手段是权重量化与更低精度加载;KV Cache 行以青色高亮:随并发乘上下文长度增长、按层数乘 K/V 两路乘头维乘精度字节估算、手段是 PagedAttention 消除浪费、KV 量化与更短上下文;激活行:随 batch 与算子实现波动、按峰值中间张量估算、手段是连续批处理摊薄与算子融合;开销行:随运行时与显存碎片波动、按总量的零头预留、手段是显存池化与固定块分配。矩阵下方一条决策链卡片:先定位最大头,再选对应手段,最后验证吞吐与延迟是否达标。底部总结条:显存问题不是「够不够」而是「哪本账超了」——四本账各有各的尺子,也各有各的刀。 图 2:四本账的对照矩阵与决策链。

一个容易算漏的细节:KV 头数不等于注意力头数。

用 GQA 的模型里 KV 头数远小于查询头数,套公式时若拿错数,KV Cache 能高估好几倍。估算前务必先查模型配置里的 num_key_value_heads,而不是想当然地等于注意力头数。

决策链:先定位最大头,再选刀。

显存问题从来不是「够不够」,而是「哪本账超了」。参数超了上量化;KV Cache 超了上分页管理、KV 量化、GQA 或压上下文;激活和开销超了上连续批处理与算子融合。动手之前先按公式估一遍每本账的量级,再决定砍哪里——估算能力本身就是面试考察点。你部署模型时最先撞上的是哪本账?用的什么手段压下去的?欢迎交流实战经验。