本地大模型提速:llama.cpp 投机解码实测,改代码快 3 倍、问答反而变慢

本地跑 8B 模型,每秒五六个字往外蹦,让它改一段代码要等一分多钟。llama.cpp 里有个开关能让这件事快两三倍,不用换显卡,也不用换模型——前提是你的任务「好猜」。
投机解码省的是大模型「一个字一个字算」的时间
大模型生成文字时,每出一个 token 都要把整个模型过一遍;但一次性检查一串现成的 token,花的时间和算一个差不多。投机解码就利用这一点:先用很便宜的办法猜出后面几个 token,再让大模型一次性验证,猜对几个就直接收下几个,猜错的位置由大模型自己算。
猜的办法有两类。一类是草稿模型:找一个同家族、同词表的小模型(本文用 Qwen3-0.6B 给 Qwen3-8B 打草稿)。另一类不需要额外模型:从已经出现过的文本里找重复片段当草稿,llama.cpp 里叫 n-gram 系列,比如 ngram-mod。
投机解码不让模型变聪明,只让它少做无用功:猜得越准,省得越多;猜不准,反而白算。
开始前准备好这些
- 适合谁:已经在本地跑大模型(Ollama、llama.cpp 都行),主要拿它改代码、改格式、做摘要的人
- 机器:本文实测为 8 核 Intel Xeon、15 GB 内存、无显卡的 Linux 云主机;8B 模型 Q4_K_M 量化文件约 5.0 GB,草稿模型 0.64 GB
- 软件:llama.cpp 预编译版 b11748(版本号 0.7.0-dev,commit 3bb63cb,10-20 发布),无需自己编译
- 时间:下载约 5 分钟,跑完对比约 15 分钟
四步跑通:下载、基线、草稿模型、n-gram
第 1 步:下载 llama.cpp 和两个模型。草稿模型必须和主模型用同一套词表,同家族的大小两个型号最省事。
curl -LO https://github.com/ggml-org/llama.cpp/releases/download/b11748/llama-b11748-bin-ubuntu-x64.tar.gz
tar xzf llama-b11748-bin-ubuntu-x64.tar.gz
curl -LO https://huggingface.co/Qwen/Qwen3-8B-GGUF/resolve/main/Qwen3-8B-Q4_K_M.gguf
curl -LO https://huggingface.co/Qwen/Qwen3-0.6B-GGUF/resolve/main/Qwen3-0.6B-Q8_0.gguf
macOS、Windows 和带 CUDA 的版本在同一个 Release 页面里,换对应的压缩包即可(本文未实测)。
第 2 步:先跑基线,不开投机解码。
./llama-b11748/llama-server -m Qwen3-8B-Q4_K_M.gguf -c 8192 -t 8 --port 8099
另开一个终端发请求。Qwen3 默认会先「思考」,测速时用 enable_thinking:false 关掉,温度设 0:
curl -s localhost:8099/v1/chat/completions -H 'Content-Type: application/json' -d '{
"messages":[{"role":"user","content":"把下面代码里的单引号字符串改成双引号,其它不变:..."}],
"temperature":0, "max_tokens":400,
"chat_template_kwargs":{"enable_thinking":false}
}' | python3 -c "import sys,json;print(json.load(sys.stdin)['timings'])"
返回的 timings 里,predicted_per_second 就是生成速度(token/秒)。
第 3 步:加草稿模型。停掉服务,加两个参数重启:
./llama-b11748/llama-server -m Qwen3-8B-Q4_K_M.gguf -c 8192 -t 8 --port 8099 \
-md Qwen3-0.6B-Q8_0.gguf --spec-type draft-simple --spec-draft-n-max 8
--spec-draft-n-max 是每轮最多猜几个 token,官方默认 3。再发同样的请求,timings 里会多出 draft_n(猜了多少)和 draft_n_accepted(被大模型收下多少),两者相除就是猜中率。
第 4 步:试不用额外模型的 n-gram。
./llama-b11748/llama-server -m Qwen3-8B-Q4_K_M.gguf -c 8192 -t 8 --port 8099 \
--spec-type ngram-mod
官方文档说 ngram-mod 只占约 16 MB,适合反复改同一块代码、做摘要,以及推理模型在答案里复述思考内容的场景。--spec-default 也会默认启用它。
实测:改代码快 3 倍,开放式问答反而慢了
我们在 10 月 20 日用同一台机器测了两类任务,各输出约 250–400 token,每个配置跑 2–4 次取中位数:
- 改代码:把一段约 2500 字符的 Python 脚本里的单引号改成双引号,输出大部分照抄原文
- 开放式问答:用中文分 5 点解释什么是投机解码
| 配置 | 改代码(token/秒) | 猜中率 | 开放式问答(token/秒) | 猜中率 |
|---|---|---|---|---|
| 不开(基线) | 5.9 | — | 7.0 | — |
| 草稿模型,每轮猜 3 个 | 9.3 | 约 100% | 6.0 | 约 36% |
| 草稿模型,每轮猜 8 个 | 17.3 | 94%–99% | 4.3 | 约 16% |
| ngram-mod(无额外模型) | 13.8 | 72% | 8.6 | 未触发 |
改代码这类「答案大半是原文」的任务,每轮猜 8 个时猜中率接近 100%,速度约为基线的 3 倍;ngram-mod 不用多加载模型也有 2.3 倍。开放式中文问答就是另一回事:小模型只猜中三成左右,大模型白白验证一堆错草稿,猜 8 个时速度掉到基线的六成。ngram-mod 在问答里找不到可复用的片段,基本不出手,速度和基线相当(表里略高,属于测量波动)。
多跑几次后,我们自己的用法是:默认开 ngram-mod,几乎没有副作用;专门改代码、批量改格式时再挂上草稿模型。
踩过的 4 个坑
- 共享机器上测速很不稳:同一个基线配置,四次测出 5.1 到 8.8 token/秒,因为同机还有别的任务在抢 CPU;草稿模型每轮猜 8 个的改代码测试也有一次只有 8.7。至少跑 3 次看中位数,别凭一次结果下结论。
- 温度为 0 也不保证逐字一致:开投机解码前后,输出在几百字后开始出现细微差别;连两次都不开的基线也不完全相同。官方文档解释过,CPU 与后端的浮点计算可能不同,需要严格一致时用贪心采样并固定环境。这不是模型变笨,但做回归测试时要知道。
- 别忘了关思考:Qwen3 默认先输出一大段思考,速度和猜中率都会被搅乱,测速时要加
enable_thinking:false。 - 草稿模型要同词表:不同家族的模型词表不同,没法直接配对。最省事是用同一系列的最小号;想要更高猜中率,文档里还有 EAGLE-3、DFlash 这类专门为某个主模型训练的草稿模型(本文未实测)。
怎么算做完了
- 能在
timings里看到draft_n和draft_n_accepted,说明投机解码已生效 - 用你自己最常用的一类任务,测出开启前后的速度和猜中率各 3 次
- 猜中率高于约 70% 的任务保留草稿模型,低于 40% 的任务关掉它或只留
ngram-mod
本文未实测:显卡上的提速幅度、Ollama 内部是否支持同样的开关、EAGLE-3 / DFlash / MTP 等新型草稿方式。GPU 上大模型验证一批 token 的代价更低,结论可能和纯 CPU 不同。
最后
把你最常让本地模型干的那件事,用上面第 4 步的 --spec-type ngram-mod 跑一次,看 draft_n_accepted 有多少——数字够高,再考虑加草稿模型。
- llama.cpp 官方文档:Speculative Decoding(commit 3bb63cb 时读取)
- llama.cpp Release b11748(2026-10-20)
- Hugging Face:Qwen/Qwen3-8B-GGUF
- Hugging Face:Qwen/Qwen3-0.6B-GGUF
- 论文:Fast Inference from Transformers via Speculative Decoding(Leviathan 等,arXiv:2211.17192)
- 论文:Accelerating Large Language Model Decoding with Speculative Sampling(Chen 等,arXiv:2302.01318)
- 本文实测:2026-10-20,llama.cpp b11748,Qwen3-8B Q4_K_M + Qwen3-0.6B Q8_0,8 核 CPU、无 GPU