本地部署 llama.cpp,3.9G 盒子只能开 5.8 万

本地部署 llama.cpp,3.9G 盒子只能开 5.8 万

结论先行:按官方 12.8 万买盒子的人,实测只有 5.8 万,差 818 MiB 就是开不了

这句判断来自一次实测量出来的数:688 MiB 换成字节是 721,554,688,除以 12,339.5,得 58,474 个 token,也就是 5.8 万。

环境、命令、量出来的数都在下面,可以直接照着重跑。

697 MB 的模型文件,官方模型卡写着最大上下文 128,000。一台 3.9 GB 内存、没有 swap 的 ARM 盒子,能不能开到官方那个数?

我把这句「能不能」拆成了能量的东西:固定权重、固定线程,只改 -c,量六档上下文下的进程常驻内存,取斜率。斜率就是每 token 的 KV 字节数。

结果是 12,339.5 字节。按 config 逐层算出来的理论值是 12,288,两者相差 0.4%。所以官方那个 12.8 万不是营销数字,它确实是从这个模型的结构推出来的。

但 12.8 万 token 的 KV 要 2.90 GB。那台盒子可用内存 2,121 MB。差 847 MB。

847 MB 在别的地方是「再挤挤」,在这台机器上是开不了 ------ 它连 swap 都没有,超了就是进程被杀。

下面这条链子是我实际跑的顺序:先认结构、再量斜率、最后把数落回那台盒子。

① 先认结构:16 层里只有 6 层真的存 KV

我干的第一件事不是下模型,是读 config。因为「12.8 万上下文」这个数字能不能成立,取决于模型有没有偷偷把注意力层砍掉。

LiquidAI/LFM2.5-1.2B-Instruct 的 config.json 是这么写的:

字段 值
num_hidden_layers 16
layer_types 6 层 full_attention + 10 层 conv
全注意力层位置(0 起) 2, 5, 8, 10, 12, 14
hidden_size / num_attention_heads 2048 / 32
num_key_value_heads 8
head_dim 64
max_position_embeddings 128,000

16 层里只有 6 层存 KV,剩下 10 层是卷积层,压根没有 KV cache。这一条决定了后面所有的数。

按这个结构手算每 token 的 KV:

2(K 和 V)× 8(KV 头)× 64(head_dim)× 2(FP16 两字节)× 6(只有 6 层)= 12,288 字节。

一个 token 12 KB。12.8 万 token 就是 1.47 GiB。这个数比很多人脑子里「128K 上下文 = 几十 GB」的印象小得多,原因就在那 10 层卷积上。

但手算只是纸面。我要的是量出来的。

② 量斜率:只改 -c,看进程内存涨多少

方法很土,但结论很硬:固定模型、固定线程数、固定 prompt 长度,只改 -c,量进程的最大常驻内存(Maximum resident set size)。llama.cpp 启动时就把 KV 一次性预分配好,所以 RSS 会随 -c 单调上升,斜率就是每 token 的 KV 字节数。

bash 复制代码
# 绑 8 个核,跑完立刻看 RSS 峰值
export GGUF=lfm2.5-1.2b-q4_k_m.gguf
for C in 4096 8192 16384 32768 65536 131072; do
  printf "ctx=%s  " "$C"
  /usr/bin/time -v taskset -c 0-7 llama-cli \
    -m "$GGUF" -c "$C" -n 2 -p 8 -st --no-warmup \
    2>&1 | grep "Maximum resident set size"
done

六档跑下来:

ctx RSS(KB) 比上一档多(MiB)
4,096 1,509,012 ---
8,192 1,558,316 48.1
16,384 1,657,576 145.1
32,768 1,855,024 337.9
65,536 2,248,784 722.4
131,072 3,039,116 1,494.2

端点相减:从 4,096 到 131,072 一共涨了 1,566,826,496 字节,除以 126,976 个 token,12,339.5 字节/token。

手算 12,288,实测 12,339.5,比值 1.004。差的那 0.4% 是块对齐和分配器碎片。这说明 config 里那 6 层不是写给人看的,它真的决定了内存。

接着我又用 Q8_0 跑了一遍同一组 -c。结果是 12,338.8 字节/token ,跟 Q4 差 −0.01%。

这一条值得单独说:你把权重从 4bit 换成 8bit,文件大了 70.5%,KV 一个字节没动。 因为 KV 是以固定 FP16 存的,跟权重量化档位完全无关。想省 KV,只有两条路 ------ 改层结构,或者单独量化 KV cache。换权重量化档是最没用的一招,很多人却指望它。

③ 落回那个 3.9 GB 的盒子

数出来了,现在把它压到一台真实的机器上。

这台是端侧的 OneThingCloud OEC,4 核 ARM,总内存 3,900 MB,零 swap 。free 读出来:available 2,121 MB ,free 只剩 134 MB ,buff/cache 占着 2,265 MB。

先把纯权重的开销量准:-c 512(KV 几乎为零)时,Q4_K_M 文件 697.0 MiB ,进程 RSS 1,432.6 MiB。

RSS 是文件的两倍多。这里必须说清楚 ------ 不是「模型要两倍内存」 ,是 llama.cpp 把 mmap 的权重页和一份工作副本都算进了 RSS。所以下面用的是实测 RSS,不是「文件大小 × 2」这种拍脑袋公式。

拿 RSS 上限 = available 来推:

档位 文件 实测 RSS 剩余 能开的上下文
Q4_K_M 697.0 MiB 1,432.6 MiB 688 MiB 约 5.8 万 token
Q8_0 1,188.5 MiB 2,415.7 MiB −295 MiB 装不下

也就是说:官方标 12.8 万,这台盒子只能开到 5.8 万,不到一半。

想开满 12.8 万,KV 要 1.47 GiB,加上 1.40 GiB 的权重,得 2.87 GiB 可用内存。这台机器 available 只有 2.07 GiB。差 818 MiB。

818 MiB 在台式机上是「再插一条内存」,在这台 3.9 GB 无 swap 的盒子上是「进程被杀」。它没有 swap,超了不会变慢,会直接 OOM。

再补一个更紧的口径:如果只给它 1,500 MB 可用(buff/cache 被别人抢走一部分),Q4 只剩 67 MiB,上下文掉到约 5,700 个 token。

所以这台机器的真实区间是 5 千 7 到 5.8 万,取决于你给不给它清缓存。官方那个 12.8 万,在这台机器上不存在。

换成钱和行不行:这台盒子二手两百多块,加 4 GB 内存的钱不如直接买一台 8 GB 的。8 GB 那档能开到 5.8 万以上还有余量,而 3.9 GB 这档连官方标称的一半都到不了。

④ 反过来说:省下来的到底是哪一部分

上面一直在算「不够」,但结构上它其实是省了的。做个反事实:如果这 16 层全是 全注意力层,每 token 就要 32,768 字节,12.8 万 token 是 3.91 GiB。

实测结构只要 1.47 GiB,省了 62.3%,约等于 10/16 ------ 那 10 层卷积一点 KV 都不占。

这个 62.5% 不是优化出来的,是模型设计出来的。你选一个混合卷积的模型,等于白得 62.5% 的上下文空间;选一个全注意力的同参数量模型,同样的内存只能开四成。

这也是我建议端侧优先看 layer_types 字段的原因。参数量、文件大小都能在模型卡上看到,但「有几层真的存 KV」只有 config 里有,而它决定你能开多长的上下文。

⑤ 三个我没做成的地方

写清楚,免得你照着我的结论去踩。

一、另一个 0.8B 的模型今天完全没跑成。 qvac/BotPsy-0.8B 我在 modelscope 拿,返回 {"Code":10990101007,"Message":"获取模型文件失败,文件内容为空"};换 hf-mirror,拉到 13,272,062 字节就断了,而它应该 529,296,960 字节。所以它的 12,288 字节/token 是我按 config 算的,不是量的,本文没有引用它任何 tok/s。

二、这台盒子到今天为止还是零 tok/s,连续第四天。 原因不在内存,在 glibc。我把 DGX 上能跑的官方 arm64 预编译包经内网分发到盒子上,直接报:

bash 复制代码
llama-b12128/llama-bench: /lib/aarch64-linux-gnu/libc.so.6:
  version `GLIBC_2.38' not found
ldd (Debian GLIBC 2.36-9+deb12u13) 2.36

包 18,753,070 字节,架构是 aarch64,文件完整,差 0.02 个版本就是跑不起来。这条我上次写过一次,今天第二次独立确认。分发路径不是瓶颈,glibc 才是墙。

三、x86 对照今天没做。 我只跑了两台 aarch64,手里那台 80 核 x86 和 4 核 SSE4.2 的老机器都没参与。所以「跨架构同模型差多少」这一半仍然是空的。

还有一组数据我判废了:20 线程那台机器在测试期间跑着生产容器,loadavg 从 1.04 涨到 4.08,16 线程的 tg 反而比 8 线程高,20 线程又掉回去 ------ 物理上不可能,整组不作为结论引用。不绑核的 tok/s 在这台机器上一律不可信,这是我上次踩过、今天又验了一遍的坑。

把这几条并起来看,结论只有一句 ------ 按官方 12.8 万买盒子的人,实测只有 5.8 万,差 818 MiB 就是开不了。

参考来源:

  • LiquidAI/LFM2.5-1.2B-Instruct 与 -GGUF 两仓库逐文件字节数(2026-11-15 00:1x 用 hf-mirror API 求和,共 24 + 12 个文件)
  • LFM2.5-1.2B 官方 config.json 原值(layer_types / num_key_value_heads / max_position_embeddings)
  • 本机实测:DGX Spark #1(aarch64 20 核 / 122G,llama.cpp b12128 原生 arm64,taskset 绑核)与端侧主机(aarch64 4 核 / 3,900 MB / 零 swap)
  • 数据落盘:site_matrix/runs/J_20261115_D3/d3_ctx_kv_20261115.json、d3_summary_20261115.json、d3_clean_20261115.json

验证等级:L1 | KV 斜率为今日实机测量(六档 -c,每档取 Maximum resident set size 峰值,端点相减后除以 token 增量),Q4_K_M 与 Q8_0 两档独立复跑;权重加载 RSS 为 -c 512 实测值。config 结构为官方原值逐字段读取,非转述。端侧那台机器的 tok/s 为零 ------ 本体从未跑起来,全文不含它任何速度数字;12.8 万上下文所需内存为按实测斜率外推,不是在该机器上真开到 12.8 万。

数据说明:1 MiB = 1,048,576 字节,1 GiB = 1,024 MiB,文中不把 GB 与 GiB 混用。KV 字节数由进程 RSS 斜率反推,未读 llama.cpp 自报的 KV self size,两者本应交叉验证但今天没做。20 线程那台机器的 tg 数据因生产容器污染(loadavg 1.04→4.08)已整组判废,未引用。

声明:本文为个人实机测试记录,机器与模型均自购自装,不含任何厂商合作。测试环境与你的环境可能不同,数字仅供参考。


本文所有数字均为本机实测,非引用。环境与脚本版本见文末参考来源。 作者:丘山录 · 记录本地部署与算力实测

相关推荐
tinggu13 分钟前
从 $6.22 到 $113.96:拆解 AI 用纯 Python 在 Blender 5.2 中“手搓”百万面 3D 场景的工程真相
人工智能
Linye16 分钟前
AI 写的 HTML 原型全是空壳?聊聊真实 DOM 交互避坑
前端·人工智能
师夷长技以自愈25 分钟前
手搓 AI Agent 第 7 篇:菜做砸了别硬炒——错误分类 + 双账本熔断
人工智能·python
调接口的阿光39 分钟前
调用对话接口时输出被截断怎么办:先看 finish_reason,再决定是续写一次、缩小范围,还是换成更短的格式(Python)
人工智能·python
东鸿电子Eastron1 小时前
工厂节能降本落地指南:从精准能源计量搭建数字化管理基础
大数据·人工智能·能源·智能电表·能耗管理
师夷长技以自愈1 小时前
手搓 AI Agent 第 6 篇:先别动刀——Plan Mode 让 AI 报完方案才动手
人工智能
Sincerelyplz1 小时前
Voice Agent 实践与落地(二):从唤醒到多轮对话,拆解实时语音链路
人工智能
guuguo1 小时前
Gemini 4 屠榜后连夜切到 Antigravity,顺手用一行命令搞定全中文界面与思考
人工智能·开源·github
码云社区1 小时前
能碳管理系统开发要关注什么?能源数据、统计口径与AI分析的衔接
java·人工智能·ai·能源·能源管理系统·能碳管理系统·能碳管理系统源码