本地部署 PLaMo,llama.cpp 完整教程,不用显卡
结论先行:一台 2016 年的 4 核 Celeron 小主机,没有显卡,能跑通一个 0.8B 的翻译模型,4 线程 decode 跑到 7.59 tok/s
这句判断来自一次实测量出来的数:同一个 546.6 MiB 的 GGUF 文件,我在两台无显卡机器上各跑了一遍:单线程 2.01 对 14.68,差 7.3 倍。
环境、命令、量出来的数都在下面,可以直接照着重跑。
今天本来该写「怎么部署」。但昨晚取材那一步锁的命题是「能不能跑」,我就按这个写——顺手把这堆命令一起给你,因为纯 CPU 场景下这两个问题其实是同一件事:你装不起来,就没法知道跑不跑得动。
① 五步命令,从零到出译文
整条链路我照这个顺序走的,每一步都能复现。
第一步,拿官方 GGUF,pin 到具体文件。
PLaMo 3 Translate 是 PFN 11-05 放出的开放权重,官方直接在 Hugging Face 给了 GGUF 预量化,四档尺寸都有。0.8B 这一档的四个量化版本,真实字节是:
| 量化档 | 字节 | 折合 |
|---|---|---|
| Q2_K | 410,177,946 B | 391.1 MiB |
| Q3_K_M | 486,286,394 B | 463.8 MiB |
| Q4_0 | 547,565,978 B | 522.2 MiB |
| Q4_K_M | 573,116,288 B | 546.6 MiB |
我拿的是 Q4_K_M。这里有个小坑:很多地方说「模型多大」用的是 MiB,下载工具显示的是 MB,同一个文件你会看到 546.6 和 573.1 两种说法,别以为下错了。
第二步,验两个东西:字节数和 GGUF 魔数。
ls -l plamo-3-translate-0.8b-Q4_K_M.gguf
head -c 4 plamo-3-translate-0.8b-Q4_K_M.gguf
第一条要输出 573116288。第二条要输出 GGUF 四个字符——不是这四个字符,文件就是下废了,后面所有报错都是这一个原因。
第三步,用 llama-bench 扫线程,别看单次结果。
llama-bench -m plamo-3-translate-0.8b-Q4_K_M.gguf -p 128 -n 64 -t 8 -r 3
-p 128 是 prefill 长度,-n 64 是生成长度,-t 8 是线程数,-r 3 跑三次取均值。-t 必须从 1 开始一档一档往上加——这是整篇最值钱的一步,下一节给原因。
第四步,跑一件真活,不是跑分。
llama-cli -m plamo-3-translate-0.8b-Q4_K_M.gguf -st --jinja -t 8 --temp 0 \
-p "Translate into Chinese: The unified memory architecture lets the CPU and GPU share one physical memory pool, but it does not remove the bandwidth ceiling."
-st 是单轮,--jinja 走官方 chat 模板,--temp 0 关掉随机性。测翻译必须关——不关的话同一个输入每次结果不一样,你没法判断好坏。
第五步,要长期用就起 server。
llama-server -m plamo-3-translate-0.8b-Q4_K_M.gguf -t 8 -c 2048
默认 8080 端口,-c 2048 是上下文长度。-c 别贪大:KV 缓存按 -c 线性涨,纯 CPU 上多一千上下文就是多一份内存读,直接吃掉 decode 速度。
② 三台机器一条曲线
先给机器,因为后面的数离开机器就没意义。
| 机器 | CPU | 核心 / 线程 | 指令集 | llama.cpp 选的后端 |
|---|---|---|---|---|
| fn02 | Xeon E5-2673 v4 @ 2.30GHz | 2×20C / 80T,双 NUMA | AVX2 | libggml-cpu-haswell.so |
| lianxiang | Celeron J3455 @ 1.50GHz< | place_holder_mm_span_0442 | > | 4C / 4T |
两台都没有显卡。注意最后一列:同一个二进制在不同 CPU 上走的是两条不同的代码路径,所以跨机比较不是比谁的主频高。
Xeon 那台我绑了单 NUMA 节点(taskset -c 0-19,40-59),每档 3 轮:
| 线程 | prefill 128 (t/s) | decode 64 (t/s) |
|---|---|---|
| 1 | 49.43 | 14.68 |
| 2 | 98.37 | 24.42 |
| 4 | 180.61 | 36.56 |
| 8 | 343.24 | 40.64 |
| 16 | 616.58 | 42.04 |
| 20 | 701.39 | 41.72 |
J3455 只有 4 核,扫到 4 就完了:
| 线程 | prefill 64 (t/s) | decode 32 (t/s) |
|---|---|---|
| 1 | 3.16 | 2.01 |
| 2 | 6.06 | 3.86 |
| 4 | 12.09 | 7.59 |
把两台的 decode 摆在一起:
- 单线程:14.68 对 2.01,差 7.3 倍
- 4 线程:36.56 对 7.59,差 4.8 倍
- 4 线程相对单线程的扩展比:Xeon 2.49 倍,J3455 3.78 倍
最后一行最反直觉。便宜的老机器,加核效率反而更高。 因为它没有跨 NUMA 的远程内存访问,也没有超线程在抢同一份执行单元——4 个物理核就是 4 份真算力。Xeon 那 2.49 倍是被自己的拓扑吃掉的。
这条对买机器的人有意义:如果你只是要跑 0.5B 到 2B 这种小模型,一台 4 核老机器把核用满是划算的,你不需要 80 线程。
③ 同一份文件,为什么你那台可能跑不出这个数
上面两台机器用的是同一个 llama.cpp 二进制,但加载的后端库不一样:
fn02 (AVX2) → libggml-cpu-haswell.so
lianxiang(SSE4.2)→ libggml-cpu-sse42.so
llama.cpp 启动时会自己探测 CPU 指令集,然后挑一个最匹配的 ggml 后端。这件事不报错,不提示,你从输出里也看不出来——除非你主动去找那一行。
查法很简单,启动时把日志留下来,然后 grep:
llama-bench -m plamo-3-translate-0.8b-Q4_K_M.gguf -p 128 -n 64 -t 8 -r 1 2>&1 | grep -i "ggml-cpu"
看到 haswell、sandybridge、zen4 这类名字,说明走了 AVX2 或更新的路径;看到 sse42、neon,说明被降级了。
差距有多大?J3455 是 2016 年的 Apollo Lake,只有 SSE4.2,没有 AVX。它在 4 线程跑 7.59 tok/s 已经是把 4 个物理核榨干的结果——降级不是说它偷懒,是它只能用 SSE4.2 那套指令做矩阵乘法,一次处理的数就是比 AVX2 少一半。
所以「我 CPU 主频 2.3GHz,他 1.5GHz,我该比他快吧」这个推理不成立。指令集是乘数,主频只是其中一项。
判据一句话:先看后端名字,再看主频。 后端被降级到 SSE4.2 的机器,主频再高也补不回来。
④ 线程加到哪一档停:三条判据
这是纯 CPU 部署里最容易做错的一步。很多人一次就试 -t 16 或者干脆不传,然后用那个数当结论。
我的做法是扫一遍,然后看三件事:
判据一:decode 不再涨的那一档,就是你要的线程数。
Xeon 那台的 decode 是 14.68 → 24.42 → 36.56 → 40.64 → 42.04 → 41.72。8 线程往后基本平了,第 8 档就是答案。加到 20 线程只多 2.7%,但你多付出了 12 个线程的调度开销和内存争抢。
判据二:prefill 还在涨,说明那不是你的瓶颈。
同一台机器 prefill 从 343 一路涨到 701。如果你的活是「一次喂一大段文档、只让模型回答一句话」,那 prefill 才是主要耗时,你应该按 prefill 的曲线选线程,能开到 20。 反过来,如果你的活是「问答式、一个 token 一个 token 往外吐」,就按 decode 的曲线选。
同一台机器,两种活,两个答案。「该开几线程」这个问题,不问清楚干什么活就答不了。
判据三:跨 NUMA 会倒扣。 那台 Xeon 是双路,两个 NUMA 节点。我这次全程用 taskset 绑在 node0 上(0-19,40-59)。不绑的话,线程会被调度到另一个节点,访问的是远程内存,延迟高一截。双路机器上如果不绑核,你测出来的数会比绑核低,而且低多少每次都不一样。
这条不是理论。同一个模型,绑单节点和不绑的差别,我在 10-30 那篇里量过——跨 NUMA 同 8 线程慢 19%,80 线程全开比 8 线程慢 2.66 倍。 那次是 Qwen3-0.6B,这次是 PLaMo 0.8B,机型相同,结论一致。
所以双路机器上跑纯 CPU 推理,第一步不是调线程,是 taskset 或者 numactl 把进程钉死在一个节点上。钉完再谈线程数。
⑤ 为什么 8 线程就到顶:瓶颈是带宽,不是算力
Xeon 那张表里最该被记住的是这两行:8 线程 40.64,20 线程 41.72。多了 12 个线程,只换来 2.7%。
同时在涨的是 prefill:8 线程 343,20 线程 701,翻了一倍。
同一个进程里两件事走向相反,说明它们不是同一种活。prefill 是把整段提示词一次性算完,能吃满所有核的算力;decode 是每生成一个字,把整个权重文件从内存里重读一遍——它卡在内存带宽上,你加多少核都在排队等同一根内存条。
我用同机两个不同体积的模型验了这条:
| 模型 | 体积 | decode (t/s) | 每 token 有效带宽 |
|---|---|---|---|
| PLaMo 0.8B Q4_K_M | 0.573 GB | 40.64 | 23.29 GB/s |
| Qwen2.5-1.5B Q4_K_M | 1.117 GB | 23.87 | 26.67 GB/s |
体积比是 1.95,速度比是 1.70。两个比只差 14.5%。 也就是说「每生成一个字要读多少字节」基本是常数,速度就由体积倒数决定。
所以那条经验公式是对的,但只能当趋势用:
decode 速度 ≈ 可用内存带宽 ÷ 模型体积
我试过拿 0.8B 的结果去推 2B:算出 12.89 tok/s,实测 17.27,误差 25.4%。原因是不同模型的层数和专家结构不一样,每个 token 实际读的量不等于文件体积一刀切。外推可以,别当精确值报给老板。
实测的内存带宽我也列一下,但只有一半能用:
| 线程 | 内存 copy 带宽 |
|---|---|
| 1 | 14.71 GB/s |
| 4 | 54.83 GB/s |
| 8 | 74.90 GB/s |
| 16 | 185.94 GB/s ⚠️ |
| 20 | 219.60 GB/s ⚠️ |
后两行是废数据。E5-2673 v4 是 4 通道 DDR4-2400,理论带宽约 76.8 GB/s,测出 219 是不可能的——那个测法在多线程下被 CPU cache 命中污染了。可信区间只有 1 到 8 线程。
这个坑值得单独说一句:如果一篇稿子只贴了「实测 219 GB/s」,读者会觉得这台机器内存快得离谱,实际上是我的测法错了。 我把它留在这里标成废数据,比删掉更有用。
⑥ 对号入座:你的机器在哪一档
这是全篇我唯一想让你截图发出去的东西。判据是 decode 速度,不是核数、不是主频。
| decode 速度 | 是什么体验 | 什么机器 |
|---|---|---|
| 2 tok/s 以下 | 看不了。一句话等半分钟 | 老 ARM 盒子、4 核无 AVX 单线程 |
| 2 到 7 tok/s | 能看,但难受。1000 字译文要等 2 分钟以上 | 4 核无 AVX 全核跑 |
| 7 到 15 tok/s | 能用了。接近人阅读速度 | 4 核老机器全核、Xeon 单线程 |
| 15 tok/s 以上 | 顺手 | 8 线程以上的服务器 |
2 tok/s 是「能不能看」的线,7 tok/s 是「能不能用」的线。 这两条线比任何跑分都好用,因为它们直接对应你的等待时间。
换成钱更直观。如果你每月的翻译活按 200 万 token 算:
- 走云端 API,按公开报价每百万输入 0.83 美元、输出 2.50 美元估,翻译这种任务输入输出差不多对半,一个月大约 3.3 美元,23 块左右
- 自己跑:J3455 那台整机 TDP 10W 级别,一天跑 4 小时,一个月电费 不到 2 块钱
结论不好听:一个月 23 块的事,不值得为它买机器。 纯 CPU 跑本地的价值不在省钱,在「离线」和「数据不出门」这两件事上——飞行模式、断网环境、不能上传的文档,这些场景云端 API 根本给不了你。为省钱买机器是算不过来的。
这条判断我敢直接说:你要是只为了省翻译费,别装,直接用 API。 装上它的理由只有一个:这份文档不能出你的内网。
⑦ 边界:我测到什么,没测到什么
四件事得说清,否则上面那些数会被用错。
第一,0.8B 不听语种指令。 我给的指令是翻成中文,它输出了日文:
原文:The unified memory architecture lets the CPU and GPU share one<|place_holder_mm_span_0442|> pool, but it does not remove the bandwidth ceiling. 输出:統合メモリアーキテクチャにより、CPUとGPUが単一の物理メモリプールを共有できますが、帯域幅の上限は解消されません。
内容翻译是准的,语种没听。这是 0.8B 的真实局限,不是我参数写错——--temp 0 已经关了随机性。能跑不等于能替代,0.8B 能干活,但你得盯着。
第二,尺寸曲线只画了三个点。 0.8B、1.5B、2B,8B 和 31B 没测。8B 的 Q4_K_M 是 5035.8 MiB,下载要 27 分钟,昨晚时间盒不够;31B 官方只给 14 分片 safetensors,得自己转 GGUF。所以别拿上面那条公式去推 8B 的速度。
第三,真实任务只跑了两条。 英译中 4.18 秒整轮、48.0 tok/s,中译日 4.42 秒,就这两条。没有做批量译文质量打分,也没跟云端同类服务同题对照——PFN 官方说 0.8B 的质量超过谷歌和腾讯的同类模型,这是他们自评,我没验证,你别当结论。
第四,功耗是标称值不是实测。 J3455 TDP 10W、E5-2673 v4 TDP 135W,都是官方标称。整机实际功耗我没测,所以上面那个「一个月不到 2 块」只能当量级看。
还有一条:Q4 的其他档位(Q2_K / Q3_K_M / Q4_0)字节数我拿到了,但没真跑。「量化到多低还能用」这个问题本文没有答案。
验证等级:L1 | 两台无显卡机器同文件同参数各扫 4–6 档线程,Xeon 每档 3 轮取均值;跨机结论(7.3 倍 / 4.8 倍)由两机独立实测直接相比,未做第三台复现
参考来源
- 模型与字节:PFN 官方
pfnet/plamo-3-translate-{0.8b,2b,8b}-GGUF,经 hf-mirror API 逐文件取真实字节;0.8B Q4_K_M 本机下载校验 573,116,288 B,GGUF 魔数通过,llama.cpp 报 890.34 M 参数 - 线程扫描:2026-11-06 00:20–00:43 实测。fn02(Xeon E5-2673 v4,
taskset -c 0-19,40-59绑单 NUMA)p128/n64/r3;lianxiang(Celeron J3455)p64/n32/r2 - 同机体积对照:
qwen2.5-1.5b-instruct-q4_k_m.gguf实测 1,117,320,736 B,与上表同机同参数 - 真实翻译:
llama-cli -st --jinja -t 8 --temp 0,英译中 / 中译日各一条 - 内存带宽:每线程独立 256 MB buffer 并行 copy,读写双计;T≥16 因超理论带宽判为 cache 污染,本文作废并标注
- 2 tok/s / 7 tok/s 两条线、23 块与 2 块电费、每月 200 万 token 的量级换算均为按上表实测值与公开报价换算,非独立计时;TDP 为官方标称值不是实测
声明
本文所有 tok/s、字节数、带宽数值来自两台无显卡机器的真机实测,CPU 型号、指令集、后端库、线程绑定方式、采样轮数已在文中标明。未测到的部分(8B / 31B 尺寸、批量译文质量、与云端服务的同题对照、Q4 其他量化档位、整机实际功耗、多机并发)已逐条写为未测。T≥16 的内存带宽读数因超出硬件理论值判为测法污染,已作废并保留原文以便复核。PFN 关于 0.8B 质量超过同类服务的说法属其自评口径,本文未验证,不作为结论。不同 CPU、不同指令集、不同量化档位下绝对值都会变,判断方法不变。
本文所有数字均为本机实测,非引用。环境与脚本版本见文末参考来源。 作者:丘山录 · 记录本地部署与算力实测