先说结论:Qwen3-8B 的 Q4_K_M 配 Qwen3-0.6B 的 Q8_0 当草稿模型,深度设 8、概率下限设 0.6,改代码从 19.6 token/s 涨到 43.5,中文问答基本不掉速。整套配下来二十分钟,内存多占 1.1GB。只开投机不调参数,问答会掉到 12.3 token/s,比不开还慢。
一、八种开法,只有一种全场景不赔
测试机是 M4 的 Mac,16GB 统一内存,8 核。模型用 Qwen3-8B-Q4_K_M,草稿模型用 Qwen3-0.6B-Q8_0,上下文 8192,单槽,温度 0,关掉思考链。四类任务各跑 3 次取中位数,任务分别是把一段 Python 里的单引号改成双引号、用中文分五点解释概念、把六个人的信息抽成 JSON、把一段带数字的材料压成摘要。
| 启动参数 | 代码改写 | 中文问答 | JSON 抽取 | 长文摘要 |
|---|---|---|---|---|
| 什么都不开 | 19.6 | 19.1 | 19.5 | 19.4 |
只给 -md |
19.6 | 19.6 | 19.7 | 19.6 |
| draft-simple 深度 3 | 36.0 | 19.5 | 36.1 | 30.4 |
| draft-simple 深度 8 | 42.7 | 12.3 | 42.8 | 27.5 |
| draft-simple 深度 16 | 46.7 | 7.0 | 37.9 | 17.6 |
| 深度 8 + 概率下限 0.6 | 43.5 | 19.0 | 41.5 | 30.3 |
| ngram-simple | 57.4 | 19.8 | 19.7 | 19.7 |
| ngram-mod | 39.6 | 19.7 | 19.5 | 19.6 |
单位是 token/s。最后两行的数字是「一次请求」的口径,理由在第七节,这两行的用法和上面四行不一样。
看表能读出三件事。代码改写和 JSON 抽取这类「输出大半照抄输入」的任务,收益稳定在 2 倍以上。中文问答是唯一会倒退的任务,深度开得越狠掉得越快。把概率下限从 0 提到 0.6 之后,问答回到 19.0,代码和 JSON 的收益几乎没动,这是全场唯一一组四类任务都不赔的参数。
基线这一段可以和之前那篇MLX 跑 8B 的实测对上。同一台机器、同一个模型,llama-server 不开投机是 19.6,MLX 解码 20.3,Ollama 17.4,三个数在一个量级,说明下面所有倍数都是老实算出来的。
二、它省的是「一个字一个字算」的时间
大模型每吐一个 token,都要把全部权重读一遍。8B 的 Q4 模型是 5GB,读一次 5GB 就为了算一个 token,显卡的计算单元大半时间在等内存。一次读 8 个 token 花的时间,和读 1 个 token 差不多,这个差值就是投机解码能吃的东西。
于是做法变成:先找个便宜的东西猜出后面几个 token,再让大模型一次把这一串全验了。猜对的直接收下,猜错的位置由大模型自己写。猜得准就白赚几个 token,猜不准就是白验一遍。
猜法分两派。一派挂个同家族的小模型当草稿,另一派不挂模型,直接从已经出现过的文本里找重复片段。后一派在 llama.cpp 里叫 ngram 系列,占内存可以忽略,代价是它只在「上下文里有可抄的片段」时才出手。
三、准备两个文件,5.0GB 加 0.64GB
mkdir -p ~/models && cd ~/models
curl -L -O "https://modelscope.cn/models/Qwen/Qwen3-8B-GGUF/resolve/master/Qwen3-8B-Q4_K_M.gguf"
curl -L -O "https://modelscope.cn/models/Qwen/Qwen3-0.6B-GGUF/resolve/master/Qwen3-0.6B-Q8_0.gguf"
ls -lh *.gguf
两个文件分别是 5027783488 字节和 639446688 字节,走魔搭直链实测两分多钟下完。草稿模型必须和主模型共用一套词表,同系列的一大一小最省事,Qwen3 的主模型配 Qwen3 的最小号就行。手上只有 safetensors 的话,先按safetensors 转 GGUF那套流程转一遍,注意窄 hidden 的小模型做 K 系量化会被静默降级。
词表对不上的后果不难认。服务能起来,请求也返回 200,但日志里的接受率会趴在地板上,速度等于没开。真要从别的家族硬配,先发一条短请求看接受率再决定要不要继续。
四、第一步就踩坑:只加 -md 等于没加
网上流传的命令大多长这样:
llama-server -m Qwen3-8B-Q4_K_M.gguf -md Qwen3-0.6B-Q8_0.gguf --port 8099
这条命令能起服务,能正常出结果,速度一点没变。表里第二行就是它:四类任务全在 19.6 附近,和不开完全一样。原因是 --spec-type 的默认值是 none,草稿模型加载进来之后没人用它。按网上的说法,旧版本会自动把 -md 翻译成 draft-simple;在我这台机器的 0.9.0 构建(build 12110)上已经不会了,这类「加了参数却没反应」的差异,翻文档不如翻自己的返回体。
判断草稿模型到底有没有干活,只看返回体里的两个字段:
curl -s localhost:8099/v1/chat/completions -H 'Content-Type: application/json' \
-d '{"messages":[{"role":"user","content":"写一行 Python 打印当前时间"}],"temperature":0}' \
| python3 -c "import sys,json;print(json.load(sys.stdin)['timings'])"
draft_n 是这一轮猜了多少个 token,draft_n_accepted 是被收下多少个。只给 -md 的时候这两个字段直接是 None,一个都没猜。把它们都带上 --spec-type draft-simple 才会出现数字。
顺带说一个更硬的坑:老教程里的 --draft-max 和 --draft-min 已经被删了,现在传进去服务根本起不来,退出码 1,报错原文是 error while handling argument "--draft-max": the argument has been removed. use --spec-draft-n-max or --spec-ngram-mod-n-max。新写法是 --spec-draft-n-max。--spec-type 写错值也一样起不来,报 unknown speculative type: xxx。这两个都是启动期报错,比静默不生效好排查得多。
五、深度从 3 扫到 16:代码越快,问答越慢
--spec-draft-n-max 是一轮最多猜几个 token,默认 3。把同一个模型、同一个任务、只改这一个数,结果是这样:
| 深度 | 代码改写 | 接受率 | 中文问答 | 接受率 | 长文摘要 |
|---|---|---|---|---|---|
| 3 | 36.0 | 99.6% | 19.5 | 38.1% | 30.4 |
| 8 | 42.7 | 98.4% | 12.3 | 18.3% | 27.5 |
| 16 | 46.7 | 99.1% | 7.0 | 9.2% | 17.6 |
代码改写这一列,接受率始终在 98% 以上,猜 16 个基本全中,所以越深越快,19.6 一路涨到 46.7,2.4 倍。中文问答这一列完全反过来,接受率从 38% 掉到 9%,深度 16 时只有 7.0 token/s,是不开的三分之一多一点。
原因在「浪费」两个字。问答类文本每句话都在换措辞,0.6B 猜错是常态。深度 16 时一轮白验 16 个候选,验证本身要跑一次完整前向,等于花 8B 的算力去否决 15 个错答案。这也是为什么「深度调大」这类建议不能照抄,它只对高重复任务成立。
所以不存在一个通吃的深度值。要么按用途起两个服务,要么接受代码快、问答慢,再往下看一节。
六、把问答救回来:概率下限 0.6
llama.cpp 里有个 --spec-draft-p-min,默认 0。它的作用是在猜之前先看一眼草稿模型自己的把握,把握低于这个数就不猜了,直接让大模型正常出字。默认 0 等于「永远都猜」,所以问答任务才会白白烧算力。
llama-server -m Qwen3-8B-Q4_K_M.gguf \
-md Qwen3-0.6B-Q8_0.gguf \
--spec-type draft-simple \
--spec-draft-n-max 8 \
--spec-draft-p-min 0.6 \
-c 8192 -np 1 --jinja --port 8099
深度留 8 不动,加上这一行,中文问答从 12.3 回到 19.0,和不开的 19.1 只差 0.5%。同一次改动里,代码改写仍有 43.5,JSON 抽取 41.5,长文摘要 30.3。接受率的解释也一致:问答的接受率从 18.3% 抬到 64.7%,猜的次数从 2328 降到 459,四个任务里唯一变差的是深度带来的那点猜测量,收益是问答不再倒退。
这一组就是我给日常用的配置。四类任务里三类快 1.5 到 2.2 倍,剩下一类不赔。
七、不挂第二个模型:ngram 的边界在哪
--spec-type 还接受 ngram 系列,其中 ngram-simple 和 ngram-mod 不需要草稿模型。它们从上下文里找之前出现过的片段,直接抄后面的内容。内存代价实测可以忽略:主模型单独跑常驻 6009MB,加 --spec-type ngram-mod 是 6004MB,差在噪声里。同一台机器挂 0.6B 草稿模型则是 7112MB,多 1.1GB。
但它的收益和「有没有东西可抄」死绑。服务刚起来、上下文里只有一条提问时,ngram-mod 在代码改写上给到 39.6,其余三类任务一次都没出手,draft_n 是 None。ngram-simple 更极端,代码改写 57.4 token/s,比草稿模型还快,另外三类一动不动。
更值得记的是重复提问的效应。同一个问题连发四次,第一次 39.8 token/s,第二次 100.9,第三第四稳在 99。原因是第一次的答案进了哈希池,第二次等于照着抄。这个数字很漂亮,但它只对「反复改同一块代码」「同一份模板反复填」这类场景成立。
这条也提醒一件事:如果你的测速脚本对同一个 prompt 连跑三次取平均,ngram 系列会给你一个假的三倍。测速必须一次一个 prompt,或者每次换内容。
输出质量上 ngram 是干净的。四类任务逐字节比对,开 ngram-mod 和不开的正文完全一致,长度都是 1386、435、448、210 字符。这一点比它的速度更值得依赖。
八、值不值得开:只读返回里的两个数
不要靠体感判断,timings 里已经给了答案。每次请求结束后,draft_n 和 draft_n_accepted 一除就是接受率。我这边四类任务的实测接受率分别是 99.6%、38.1%、96.6%、77.0%,对应的速度倍数是 1.85、1.00、1.85、1.57,几乎就是一根线。
经验阈值:接受率高于七成,留着,稳稳拿到 1.5 倍以上;四成到七成,只能算不赔,要看你是否在乎那 1.1GB 内存;低于四成,关掉,深度越大亏得越多。中文问答在深度 8 时是 18.3%,属于必须关掉的那一档,加概率下限把它抬到 64.7% 才变得可用。
另外那个 1.1GB 的代价在 16GB 的机器上不算小。装着草稿模型,上下文就只能压到 8192 附近;想吃 32K 上下文,要么换 4bit 的主模型,要么退回 ngram 方案。权重和 KV 各自吃多少,可以用本地大模型显存怎么算那套斜率自己复算,草稿模型属于它没覆盖的第三块。
九、五个真实的失败点
第一个是上一节说的静默不生效。-md 加载了模型却不启用,服务日志里没有任何警告,只能靠 draft_n 判断。
第二个是旧参数直接让服务起不来。搜到的中文教程里 --draft-max 出现频率极高,这个参数在现在的构建里是硬报错,退出码 1。
第三个是输出并非绝对不变。ngram 方案四类任务逐字节一致,草稿模型方案在中文问答上出现了分叉:同一 prompt、温度 0,从第 128 个字符开始换了一套措辞,总长从 435 字符变成 426。我把 --spec-draft-p-split 从默认 0.1 改成 0 重跑,结果仍然是 426,说明不是采样随机性造成的。要做输出回归测试的项目,别把「投机解码不改输出」当保证。
第四个是内存估算偏乐观。0.6B 的 Q8_0 权重只有 0.64GB,但落地之后常驻涨了 1.1GB,多出来的部分是草稿模型自己的 KV 与 Metal 缓冲。按权重文件大小估算会少算一半。
第五个是并发下的收益衰减。投机解码吃的是「计算单元闲着」这件事,多人同时请求时计算单元本来就不闲,收益会明显变小。单机自用、单槽的情况下这张表才成立。要开多槽,先按Ollama 并发怎么设里的方法把槽位和内存的账算清楚,再决定给不给每个槽挂草稿模型。
十、FAQ
问:草稿模型能不能用更小的,比如 0.3B 或者更大量化?
能,但接受率会跟着掉。0.6B 的 Q8_0 是 0.64GB,换成 Q4 能省 0.2GB 内存,接受率通常掉几个点。真要省内存,优先考虑直接不挂草稿模型用 ngram,代价是只在重复性任务上有收益。
问:Ollama 里能不能这样配?
Ollama 的 Modelfile 有 DRAFT 和 draft_num_predict 两个字段,能把同样的东西写进模型定义里,但深度和概率下限的可调空间比 llama-server 小,也读不到 draft_n 这类统计。要调参就用 llama-server,它的 OpenAI 兼容接口在 8080 或你指定的端口,前端照样能接。
问:这套东西在独显上收益一样吗?
方向一样,幅度不同。独显的问题是草稿模型过 PCIe 的往返延迟,所以草稿模型必须整块放进显存,--spec-draft-ngl 给满。一旦草稿模型有层落在 CPU 上,省下来的时间会被传输吃掉。统一内存的 Mac 不存在这个问题,这也是本文所有数字都只在 M4 上成立的原因。
延伸阅读
- GPT-OSS开源权重模型发布:120B/20B双版本Apache 2.0可商用,128K上下文与本地部署全解析2 月前
- 本地大模型显存怎么算:8B 每 1K 上下文 144MB7 天前
- 中国电信开源 Xing4.0-29B-A4B:全栈国产,消费级显卡跑 512K 上下文2 月前
- 阶跃星辰 600B MoE 旗舰 Step 5 Preview 上线 22 小时 GPU Kernel 优化到 508 TFLOPS2 月前
继续读
往下这几篇值得接着看:
- Ollama 函数调用怎么配:七个不报错的坑2026-11-15
- PDF 转 Markdown 选 docling 还是 MinerU:三份文件实测2026-11-15
- LiteLLM 代理 Ollama 怎么配:五处静默失败实测2026-11-15
想快速查定义?对应的词条在这里:
- Llama词条 · AI 自学笔记
