“8 GB 内存减去 2 GB 模型文件,还剩 6 GB。”这道减法不能用来选设备。 模型文件是存储制品;推理时还会驻留权重、分配 KV cache 和计算缓冲,操作系统及检索、语音、显示服务也在使用内存。即使一条短请求成功,两个长会话同时到来时仍可能失败。
现在就能拿到的数字只有两类:固定版本的模型文件有多少字节,以及按模型结构和假设请求长度算出的 KV 理论值。工作区、系统占用、分配策略和峰值恢复要在目标整机上测。本文用一个假设的 2 GB 文件与 8 GB 设备问题演示如何把这些数放在同一张账里;不声称任何具体板卡已经运行通过。
“8 减 2”错在把两种账混成了一种
文件留在磁盘上不代表推理不占 RAM;反过来,文件大小也不等于进程峰值。一个较有用的问题是:设备在运行同一模型、同一配置、同一组真实任务时,物理内存究竟被谁占着?

图只区分对象,不按比例表示容量。顶部留白是尚未分配的余量,不是已占用的第五项;第二会话需要的缓存若实际分配,会挤占同一块物理 RAM。
| 要记的账 | 现在可以知道什么 | 还不能据此宣布什么 |
|---|---|---|
| 权重制品 | 固定 hash 的文件字节数、量化格式、加载参数 | 文件大小不是运行峰值 |
| KV cache | 层数、KV 头数、头维、K/V 精度和分配 token 数 | 理论张量大小不是分配器实际占用 |
| 计算工作区 | 运行时日志可能给出图缓冲或批处理缓冲 | 不存在通用的“权重乘几倍”系数 |
| 系统与伴随任务 | 目标镜像和服务启动后可以读取可用内存 | 标称 8 GB 不是模型可独占的 8 GB |
| 并发与余量 | 业务可给出会话上限及最长请求 | 两个会话不一定复制整份权重,也不一定免费共享 KV |
这里还有一个容易误导评审会的数字。假设某模型恰有 30 亿参数,且每个参数都恰好占 4 bit,裸权重算术下界是 3,000,000,000 × 4 ÷ 8 = 1,500,000,000 bytes,约 1.40 GiB;8 bit、16 bit 的对应下界约为 2.79 GiB、5.59 GiB。它不是实际 Q4 文件大小,更不是设备需求。量化块的 scale、零点、高精度张量、头部和对齐都会改变制品大小;实际比较必须回到固定 hash 的文件及加载日志。不要把十进制 GB 与二进制 GiB 写成同一个单位。
权重量化只直接改变权重账。它不会按同一比例缩小系统服务、KV cache 或计算缓冲。llama.cpp server 文档把上下文、K/V 缓存类型、设备卸载与模型加载分别配置,正是因为这些占用不是一个“4-bit”标签能概括的。
内存映射没有免掉物理内存
运行时即使用内存映射读取模型,访问过的文件页也可能驻留并由进程共享。单进程 RSS 可能包含共享页,直接相加会重复计算;Linux 的 smaps_rollup 文档中的 PSS 可以按比例分摊共享映射。但 PSS 仍不能回答独立显存、容器限制或设备分配器是否够用。容量记录应同时保留进程、系统及设备内存的口径和采集时刻。
512 MiB 从哪里来:把 KV cache 算式摊开
自回归模型生成后续 token 时会复用此前 token 的 key 和 value;Hugging Face 对缓存机制的说明解释了逐层保存 K/V 的原因。对每个注意力层形状一致、K 与 V 精度一致且没有特殊窗口的模型,可先算:
KV 字节数 ≈ 2 × 注意力层数
× KV 头数 × 每头维度
× 每会话已分配 token 数
× 每元素字节数
× 同时独立分配的会话数
假设 32 层、8 个 KV 头、每头 128 维,K/V 都按 2 bytes/元素保存,一个会话分配 4096 token。代入后是 2 × 32 × 8 × 128 × 4096 × 2 = 536,870,912 bytes,即 512 MiB。若两个会话各自完整分配相同长度,理论 KV 基线为 1024 MiB;把每会话上限改成 8192 token,其他条件不变时这一项再翻倍。
这几个数字是纯计算算例,不是某个模型或板卡的实测。公式里的“8”是 KV 头数,不一定等于配置中的 query heads;GQA/MQA 模型尤其不能照抄 query heads。K/V 精度不同就分开计算两项。运行时可能预分配、按需扩展、复用槽位或按页分配,计算结果只能作为核对日志的基线。滑动窗口、混合注意力或跨层状态复用也需要按真实模型结构重新计算,不能强行套统一层数。
两个会话并不总是“乘二”
继续用上述假设。若会话 A 分配 4096 token,会话 B 只分配 2048 token,且两者各自保存独立 K/V,那么理论缓存是 512 MiB 加 256 MiB,即 768 MiB。写成“并发数 2 × 512 MiB”会高估这个配置;若运行时为两个槽位都预留 4096 token,又不能拿 768 MiB 当保证。评审表里应填每个槽位的有效分配长度和运行时报告的实际分配,不是只填“同时两人”。
反过来,声称“并发共享模型,所以内存几乎不变”也不成立。共享权重与共享缓存是两回事:两个对话的历史 token 不同,缓存通常要分别维持;批处理策略还会影响计算工作区。把每条请求的开始、结束及分配长度记录下来,才能解释峰值为什么出现在某一秒,而不是出现在模型刚加载时。
改变 K/V 精度、把缓存从 GPU 卸到 CPU,可能减少某一处的名义占用,却会带来 scale、转换缓冲、传输或延迟代价。Hugging Face 的缓存策略文档把量化和卸载都列为取舍,而非免费容量。比较两次结果时,要把权重制品、K/V 类型、上下文、并发、卸载设置和运行时版本放在同一行;只写“4-bit”无法说明到底改了哪本账。
例如只把权重从 8 bit 换为 4 bit,算术下界看似减少约 2.79 GiB 到 1.40 GiB 的差额,却不能把同样的差额从整机峰值里直接扣除。原来的 8-bit 和新的 4-bit 制品可能具有不同元数据与高精度张量;后端也可能改变加载或卸载策略。正确的比较是锁定请求、运行时和设备,再观察同一口径的加载后与请求峰值。若答案质量降到业务不能接受,省下的 RAM 也不是收益。
让 4096 token 变成一条真实请求
算式里的 token 上限不是用户界面显示的“字数”。假设业务最长输入 3000 token、回答最多 800 token,系统提示和模板再占 200 token,合计已达 4000 token。若运行时上下文设为 4096,检索片段多出 150 token,先发生的可能是截断,而不是 OOM。把上限调到 8192 可以避开这次截断,但某些运行时会因此提前保留更多 KV 内存。
所以不能只拿一条 100 token 问答证明“8 GB 能跑”。至少要区分短请求、最长单会话和真实重叠的并发请求。若产品还要本地检索或驱动屏幕,测试时不能把这些进程关掉;那样测到的是缩减后的演示机,而不是交付设备。并发也不能用“单会话峰值 × 线程数”代替,因为权重可能共享、缓存可能复用,工作区与排队策略又会变化。
这一节的关键输入不是“模型是 7B”之类的简称,而是实际分配的上下文、最长输入与输出、同时活跃的会话数。有了这些输入,才能判断前述 512 MiB 算例在目标任务中是偏小、偏大,还是根本不适用。
还有一个常被忽略的分叉:业务说“两个用户同时在线”,不等于两个用户同时占满长上下文。容量验收应选择一个可解释的并发负载,例如两个最长请求在输入处理阶段重叠,而不是让第二个请求等第一个完成。与此同时,也别把偶然最坏情况当成无条件规格;如果产品通过队列明确限制同一时刻只处理一个长请求,就应把队列等待与超时一并写进验收条件。省内存换来的响应延迟,必须由产品负责人接受,而不是被测试脚本藏掉。
留空格,不填想象中的“安全余量”
下面这张表用于一个候选配置的一次测量。先固定模型与 tokenizer 版本、制品 hash、量化格式、运行时构建、后端、目标镜像和伴随服务。每格填数值、MiB 单位、采集时间及原始日志;没有测量就保留空白。理论项和观测项并排,不互相冒充。
| 观测项(MiB;各行不能相加) | 静态估算 | 模型加载后 | 最长单请求 | 目标并发峰值 |
|---|---|---|---|---|
| 模型制品 | 文件字节换算:____ | 同一制品 hash:____ | 同左 | 同左 |
| 模型文件映射(若可单独识别) | 不由文件大小推断 | 映射 RSS/PSS:____ | 映射 RSS/PSS:____ | 映射 RSS/PSS:____ |
| KV cache | 结构公式:____ | 运行时分配:____ | 运行时分配:____ | 运行时分配:____ |
| 计算、图与批处理缓冲(运行时可报告时) | 待测 | ____ | ____ | ____ |
| 推理进程总 RSS/PSS | 不能由上面各项相加 | ____ / ____ | ____ / ____ | ____ / ____ |
| 系统及伴随任务、容器/设备限制 | 待测 | 服务状态与限制:____ | ____ | ____ |
整机峰值 / 峰值同时刻 MemAvailable |
不能由文件推断 | ____ / ____ | ____ / ____ | ____ / ____ |
进程总 RSS/PSS 已包含可归属该进程的映射、缓存和缓冲;上面的细分行是解释线索,不是可加总的独立费用。 模型文件字节也不能从进程 RSS 中直接相减。采集整机峰值时,同步记录请求 ID、实际输入/输出 token、开始与结束时间、是否真正重叠、容器限制、交换/OOM、失败和取消请求。只保存成功请求,会把最重要的容量边界删掉。
执行顺序可以很短:目标镜像先启动正常服务,记录空载;加载固定 hash 模型后记录运行时报告的权重、KV 和工作区;再跑最长单请求与真实并发;请求结束后观察内存是否回到预期范围并重复几轮。采样至少保存 /proc/meminfo 的 MemAvailable、推理进程的 smaps_rollup RSS/PSS、容器内存当前值及限制(若使用容器),并在运行时提供时保存其 KV/工作区分解和设备内存计数。每次采样附时间戳;若运行时没有报告某项,写“未报告”,不要拿文件大小补空。对比两种量化格式时只改制品和必要加载参数;同时改模型、上下文、并发和运行时版本,结果就失去归因能力。
从一行未完成的预算,读出下一步
假设目前只取得“8 GB 设备”“2 GB 模型文件”和前述 512 MiB 理论 KV。表中静态估算可以填文件字节换算与公式;加载后、最长请求和目标并发三列必须留空。此时能说的是:模型存储有候选,缓存有一个待核对的理论起点。不能写“还剩约 5.5 GB”,因为权重驻留、工作区、系统服务、容器限制和分配策略都没有进入同一口径。
拿到第一次目标机记录后,也不急着把表格里所有空格换成一个总数。若加载后系统可用内存明显下降,要先分辨共享文件页与独占缓冲;若最长请求峰值比理论 KV 高,检查运行时是否预留更长上下文、是否还有批处理缓冲,以及该峰值是否与其他服务同时发生。记录无法解释的差额比“调整估算系数让表格对齐”更有价值,它会决定下一轮应抓哪段日志。
真正可以比较的两行,应固定设备、系统镜像、运行时版本和请求集合,只改变一个候选配置。若要比较 3B 与 7B,必须重新读取各自模型结构,不能沿用 3B 的每 token 缓存常数;若要比较两种量化格式,至少确认 tokenizer、上下文和 K/V 精度未在背后跟着改变。本文没有这样的成组实测记录,因此不提供“哪个模型更适合 8 GB”的排行榜。
失败时先问“卡在哪一段”,别立即买更大内存
同样是“模型跑不起来”,有的失败发生在输入尚未送进模型前,有的出现在加载时,有的只在两条请求重叠时出现。下面的图按可观察到的现象分叉,避免把所有问题都归为“RAM 不够”。
留住失败前最后一个成功动作:加载完成但首条请求失败,查首次输入处理与分配;短请求成功、长请求被截断,查 tokenizer 与输出预留,增加 RAM 未必有用;并发才 OOM,查重叠时段的 KV 分配、容器与设备内存限制;第一轮成功、后续轮次逐渐失败,查缓存回收或任务积压。每条分支只改一个变量后重跑原负载,才能知道修改是否解决原问题。
判读计数时先确认内存拓扑。统一内存设备上,进程 RSS、GPU API 计数和系统 available 可能描述同一批物理页,不能直接相加;独立显存设备可能是 RAM 尚足、VRAM 已满。只有一句 OOM 而没有请求形状和有效配置,不能推断“至少需要 16 GB”。权重量化或缩短上下文也可能伤害答案质量、首字等待或必要的手册片段;容量过关后仍须用业务问题集和TTFT、TPOT、功耗验收方法检查可用性。
什么时候可以对 8 GB 说“够”
只有在固定模型制品、有效运行配置与目标负载之后,最长请求和目标并发都通过,整机峰值、失败、交换/OOM 与恢复记录满足产品要求,才能对这台设备、这组条件说“够”。若只拿到文件大小和理论 KV,可用于筛掉明显不合理的候选,却不能签容量验收。若产品必须同时跑长手册检索、显示和多个会话,而降低上下文或并发会破坏需求,就应比较更大内存或不同部署路线;选型顺序见边缘大模型硬件选择。
本文没有目标板、两种模型规模或两种量化格式的实测记录。30 亿参数、512 MiB KV 和 2 GB/8 GB 问题都用于演示计算与验证方法;它们不是 RK3588、GPU 或任何 AIHub 设备的性能结论。采购沟通里最诚实的一句结论可以是:“理论账已算,整机并发峰值仍待测。”这比一个没有负载、版本和失败记录的“能跑”更有用。