推理引擎加速开关导致MoE模型能力显著下降实测
Is your engine lobotomizing your model? Kinda, and I measured it
本地部署MoE模型的同学必看,这篇用数据揭示了所谓“加速黑科技”的真实代价,特别是小显存下的精度崩塌,建议对照调整你的推理参数。
I've been reading a lot about "engines lobotomizing the models they run", either because they quantize already quantized weights, or because they automatically turn on "hacky" flags to gain decode/prefill performance by default. I wanted to test this out by myself, not only with KLD but with real tasks, so I set up an experiment using Qwen 3.8 Flash Next with Basalt (disclaimer: I'm the developer) and two of the most popular "speed" flags:
我一直在阅读大量关于“引擎对运行的模型进行脑叶切除”的讨论,原因要么是引擎对已经量化的权重再次量化,要么是因为它们默认自动开启一些“投机取巧”的标志位,以换取解码/预填充性能的提升。我想亲自测试这一点,不仅使用 KLD 指标,还要结合实际任务,所以我搭建了一个实验,使用 Qwen 3.8 Flash Next 和 Basalt(免责声明:我是该项目的开发者),并启用了两个最受欢迎的“速度”标志位:
- Routing bias: the engine pushes the model toward experts that are already on the GPU, so it sometimes gets a convenient expert instead of the one it actually wanted.
- Tail skip: when one of the model's lower expert picks isn't on the GPU, the engine just skips it instead of fetching it.
- 路由偏差(Routing bias):引擎会将模型推向那些已经加载在 GPU 上的专家,因此有时它会得到一个方便的专家,而不是模型真正想要的那个。
- 尾部跳过(Tail skip):当模型选择的某个较低优先级的专家不在 GPU 上时,引擎会直接跳过它,而不是去获取它。
Both are faster because fewer experts come from RAM, but they change the output. I added them to the engine as opt in flags so I could measure the real cost with everything else fixed.
两者都能带来更快的速度,因为来自 RAM 的专家减少了,但它们会改变输出结果。我将它们作为可选标志位添加到引擎中,以便在其他条件不变的情况下衡量其真实成本。
DISCLAIMER: This post and the experiment were run by me, but it has been polished by AI.
免责声明:本文及实验由我执行,但经过 AI 润色。
Rig: RTX 5090 + RTX 5060 Ti and 128 GB of DDR5. Settings tested:
硬件配置:RTX 5090 + RTX 5060 Ti 和 128 GB DDR5 内存。测试的设置如下:
- Shipped: bias 1.5, skip rank ≥ 7. Values other engines ship (or have shipped) as defaults on smaller cards.
- Worst: bias 16, skip rank ≥ 1, plus relaxed draft acceptance. Worst case scenario, not something anyone ships or would make sense to use.
- 出厂设置(Shipped):bias 1.5,skip rank ≥ 7。这是其他引擎在小显存卡上作为默认值提供(或曾提供)的值。
- 最差情况(Worst):bias 16,skip rank ≥ 1,以及放宽的草稿接受策略。这是最坏的情况,并非任何引擎会提供或建议使用的设置。
1. KLD
100 chunks of wikitext, decode path, against a llama.cpp Q8 reference. Exact IQ3_XXS and Q2_0 are in there for scale.
100 个 chunk 的 wikitext,解码路径,与 llama.cpp 的 Q8_0 参考模型对比。其中包含了精确的 IQ3_XXS 和 Q2_0 用于比例参考。
| Pack + setting | File | PPL | KLD mean | Same top-1 |
|---|---|---|---|---|
| Q8_0 exact | 178 GiB | 3.276 | 0.031 | 94.1% |
| NVFP4 exact | 122 GiB | 3.304 | 0.068 | 91.4% |
| IQ3_S exact | 81 GiB | 3.385 | 0.155 | 87.3% |
| IQ3_XXS exact | 74 GiB | 3.470 | 0.243 | 84.0% |
| IQ3_S + shipped | 81 GiB | 3.583 | 0.244 | 84.3% |
| NVFP4 + shipped | 122 GiB | 3.931 | 0.348 | 81.8% |
| Q8_0 + shipped | 178 GiB | 4.221 | 0.401 | 79.4% |
| Q2_0 exact | 65 GiB | 3.949 | 0.436 | 78.2% |
| NVFP4 + worst | 122 GiB | 5.917 | 0.823 | 71.3% |
| Q8_0 + worst | 178 GiB | 12.858 | 1.616 | 56.8% |
Note: even exact Q8_0 is 0.031 away from the reference. That's mostly the reference's own noise, not damage: llama.cpp's Q8_0 rounds a few spots of this model more than Basalt does, which adds about 0.01-0.02 to every row equally, so the rows still compare fairly.
注意:即使是精确的 Q8_0,也与参考值相差 0.031。这主要是参考模型自身的噪声,而非损害:llama.cpp 的 Q8_0 对该模型的某些部分舍入程度比 Basalt 更大,这给每一行都均匀增加了约 0.01-0.02 的值,因此各行之间的比较仍然公平。
2. Does it really matter?
2. 这真的重要吗?
KLD by itself is just a number, so I also ran a task eval: thinking off, temp 1.0, top_p 0.95, top_k 20, two different sets of questions:
KLD 本身只是一个数字,因此我还运行了一项任务评估:关闭思维链,温度 1.0,top_p 0.95,top_k 20,两组不同的问题:
- Ladders: 300 chained steps, scored by step.
- Facts: 1200 questions with 5 popularity bands (rare to famous), 3 seeds.
- 阶梯测试(Ladders):300 步连锁步骤,按步骤评分。
- 事实问答(Facts):1200 个问题,分为 5 个流行度层级(从冷门到著名),3 个随机种子。
| Config | Ladders | Facts | Ladders Δ | Facts Δ | Prose tok/s |
|---|---|---|---|---|---|
| Q8_0 exact | 45.4 | 38.1 | 117 | ||
| NVFP4 exact | 45.6 | 38.0 | 272 | ||
| Q8_0 + shipped | 43.8 | 36.6 | -1.6 [-3.4, +0.2] | -1.5 [-2.5, -0.5] | 202 |
| NVFP4 + shipped | 44.1 | 36.8 | -1.5 [-3.2, +0.2] | -1.2 [-2.2, -0.4] | 271 |
| IQ3_S exact, small card* | 44.1 | 36.7 | 192 | ||
| IQ3_S + shipped, small card* | 38.9 | 34.3 | -5.2 [-7.1, -3.2] | -2.3 [-3.4, -1.3] | 299 |
| Q8_0 + worst | 26.6 | 2.7 | -18.8 | -35.3 | 379 |
| 配置 | 阶梯表 | 事实准确率 | 阶梯表 Δ | 事实准确率 Δ | 每秒词元数 |
|---|---|---|---|---|---|
| Q8_0 精确值 | 45.4 | 38.1 | 117 | ||
| NVFP4 精确值 | 45.6 | 38.0 | 272 | ||
| Q8_0 + shipped | 43.8 | 36.6 | -1.6 [-3.4, +0.2] | -1.5 [-2.5, -0.5] | 202 |
| NVFP4 + shipped | 44.1 | 36.8 | -1.5 [-3.2, +0.2] | -1.2 [-2.2, -0.4] | 271 |
| IQ3_S 精确值,小卡* | 44.1 | 36.7 | 192 | ||
| IQ3_S + shipped,小卡* | 38.9 | 34.3 | -5.2 [-7.1, -3.2] | -2.3 [-3.4, -1.3] | 299 |
| Q8_0 + worst | 26.6 | 2.7 | -18.8 | -35.3 | 379 |
* Small card: the 5090 alone capped to 16gb, the experiment was run this way because it was still much faster than the 5060 Ti.
* 小卡:仅 5090 被限制在 16GB,实验采用此方式是因为其速度仍远快于 5060 Ti。
On a big card the shipped flags barely matter (NVFP4: 272 -> 271 tok/s) and cost about 1.5 points, 4% of the facts the model knew. On a small card you get +56% speed but a much bigger hit. At the worst setting facts fall from 38% to 3%, and the model says things like "The Great Composer" with total confidence. Easy tests barely notice. The damage is mostly in chained reasoning and recalled facts.
在大显存卡上,shipped 标志的影响微乎其微(NVFP4:272 -> 271 tok/s),代价约为 1.5 个点,即模型已知事实的 4%。在小显存卡上,你获得了 +56% 的速度提升,但付出了更大的代价。在最差设置下,事实准确率从 38% 跌至 3%,模型会自信地输出诸如“伟大的作曲家”之类的内容。简单测试几乎察觉不到问题。损害主要出现在链式推理和回忆事实中。
3. Bonus: a loop in the model weights
3. 附加内容:模型权重中的循环
Not every bad output is the engine's fault. One prompt made Qwen 3.8 Flash Next get stuck into an endless run of 0 in its reasoning:
并非所有糟糕的输出都是引擎的错。有一个提示让 Qwen 3.8 Flash Next 陷入无限输出 0 的推理循环:
| Setup, same request, seeds 1-4 | Stuck in zeros |
|---|---|
| Basalt, NVFP4 | 4 of 4 |
| Basalt, IQ3_S | 4 of 4 |
| Basalt, Q8_0 | 3 of 4 |
| llama.cpp, same IQ3_S GGUF | 3 of 4 |
| API (OpenRouter - Alibaba) | 6 of 6 |
| 设置,相同请求,种子 1-4 | 陷入零值 |
|---|---|
| Basalt, NVFP4 | 4 of 4 |
| Basalt, IQ3_S | 4 of 4 |
| Basalt, Q8_0 | 3 of 4 |
| llama.cpp, 相同 IQ3_S GGUF | 3 of 4 |
| API (OpenRouter - Alibaba) | 6 of 6 |
The odds of another 0 are the same on every quant and both engines: 0.99 after 10 zeros, 95%+ past 100 or so. top_p 0.95 leaves 0 as the only choice, and temp 1.0 doesn't fix it. The API fixes it by cutting the run at ~500 zeros. Basalt patched the issue as well, after 256 identical reasoning tokens it closes the thinking block and keeps going.
在所有量化方法和两种引擎中,下一个输出为 0 的概率是相同的:连续 10 个零后为 0.99,大约 100 个零后超过 95%。top_p 0.95 使得 0 成为唯一选择,而 temp 1.0 无法解决此问题。API 通过在约 500 个零时截断输出来修复此问题。Basalt 也修补了该问题,在生成 256 个相同的推理 token 后,它会关闭思考块并继续运行。
TLDR: there's no free lunch.
TLDR:天下没有免费的午餐。
- Engines can buy speed on MoE models by turning on speed "hacks".
- With values some engines ship, every pack drops to somewhere between IQ3_XXS and Q2 on KLD. A Q8 ends up next to Q2. This is as bad as it gets.
- In practice the model forgets things it knew and loses the thread on multi step problems. Small cards get the biggest speedup and pay the most for it.
- If your engine is faster than it should be on a model that doesn't fit, check whether it's still running the model you downloaded.
- 对于 MoE 模型,引擎可以通过开启速度“黑客”手段来换取速度。
- 使用某些引擎提供的 shipped 值时,每个包在 KLD 指标上都会降至 IQ3_XXS 和 Q2 之间的某个位置。一个 Q8 最终会靠近 Q2。这已经是最坏的情况了。
- 在实践中,模型会遗忘已知的知识,并在多步问题上迷失方向。小显存卡获得的加速最大,为此付出的代价也最高。
- 如果你的引擎在处理不适合的模型时比预期更快,请检查它是否仍在运行你下载的模型。
更进一步:量化金融体系
看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力