AI Pulse

DFlash 2基准测试:真实编码提速2.26倍,叠加n-gram达4.68倍

大家好,

几天前,Inco AI 发布了 DFlash 2,附带 Qwen 3.8 27B 的草稿模型和一个 llama.cpp PR。我构建了该 PR,并针对普通解码、MTP、n-gram 查找草稿,以及我七月在 Qwen 3.6 27B 上的 DFlash 1 数据进行了三天测试。使用一块 RTX PRO 6000,并发 1,大约运行了三天。

有趣的结果不是我测到的最大数字,而是 n-gram 在哪些地方真正有用、在哪些地方没有用。

摘要

* 单独使用 DFlash 2:在 100 个真实 LiveCodeBench 问题上达到 2.26 倍(67.97 → 153.91 tok/s,token 间延迟 14.27 → 6.02 ms),自然结束,没有任何强制。这是重点。额外占用 +2.7 GB 显存。
* DFlash 2 + 一个 n-gram 查找表(ngram-map-k4v):在一个 18 轮编码会话的构建阶段达到 4.68 倍(65.1 → 304.9 tok/s)。添加第二个表(ngram-mod)反而变慢,为 3.77 倍。七月时,使用 DFlash 1,叠加两者是赢家。我没想到这一点会反转。
* 同一个 n-gram 标志在合成基准上是 +52%,在 LiveCodeBench 上是 +1%,在散文上是 -30%。+52% 是测试框架退化导致的,不要引用它。
* 推荐的 --spec-draft-n-max 7 已经过了峰值。在 8K 编码提示上,5 大约多了 11%。7 也是一个硬上限(block_size 8),超过任何值都会被静默截断。
* --spec-draft-p-min 在 DFlash 2 上不起作用。common/speculative.cpp 中的 DFlash 2 代码路径从不读取它。
* 我还在一个合成测试中测到了 8.47 倍。我差点用它作为标题。这主要是模型陷入重复循环导致的基准垃圾数据。

测试设置(对复现重要的部分)

* 目标模型 ggml-org/Qwen3.8-27B-GGUF:Q4_K_M(18 GB)。草稿模型 incoai/Qwen3.8-27B-DFlash2-GGUF:Q4_K_M(1.1 GB)。MTP sidecar mtp-Qwen3.8-27B-Q8_0.gguf(3.0 GB)。关闭推理模式。
* llama.cpp b10498 从 PR #27342(commit 5ecbe1ac)构建,CUDA 13.3。PR 构建在非推测基线上与上游 b10499 在 0.3% 以内匹配(仅检查了 512 和 4K)。
* RTX PRO 6000 Blackwell 96 GB,Ryzen 9 9950X。-c 262144,f16 KV,-fa on-ngl -1,草稿模型完全在 GPU 上。
* 所有地方并发 1。除多轮编码测试框架外,所有测试都使用贪婪采样(温度 0),多轮编码测试框架使用模型默认采样且无种子(详见下文)。
* 同一时间 GPU 上只运行一个服务器(flock),每个配置使用全新容器,配置之间将显卡冷却到 45 °C,上下文大小之间冷却到 60 °C。11.6 小时的遥测,零降频事件,因此显卡处于功耗限制,而非热限制。
* 全上下文。

1. DFlash 2 将真实编码吞吐量提升一倍以上,并以一半显存在同草稿宽度下击败 DFlash 1

100 个 LiveCodeBench 问题陈述按相同顺序重放,流式输出,没有 ignore_eosmin_tokensmax_tokens。每个答案都在模型自然结束的地方结束。

tok/s相对自身基线ITL墙钟时间
Qwen 3.8 27B,无推测67.971.00x14.27 毫秒
+ DFlash 2 (n=7)153.912.26x6.02 毫秒
+ DFlash 2 + 两个查找表155.832.29x6.11 毫秒
Qwen 3.6 27B,无推测67.751.00x14.34 毫秒
+ DFlash 1 (n=7,匹配)135.342.00x6.93 毫秒

DFlash 1 在 n=7 下重新运行,因为在其自身的最大值 15 下比较会衡量上限,而不是草稿模型。在匹配宽度下,DFlash 2 领先,相对于各自模型的基线为 2.26x 对 2.00x,探针接受率为 60% 对 48%,并且它占用 +2,720 MiB,而 DFlash 1 在七月占用 +5,554 MiB。部分内存差距是量化选择(Q4_K_M 1.1 GB 草稿模型 vs Q8_0 1.8 GB)造成的,而非架构问题。

有两件事需要小心。跨代的行不是受控 A/B 测试:不同的模型、不同的目标量化、不同的草稿量化(而且草稿量化对 DFlash 2 是不利的,而非有利)。比较加速比,绝对不要比较绝对 tok/s;两个基线相差 0.3% 纯属运气。

声明 vs 实测:Inco AI 声称在 SGLang 上批次大小为 1 时,该模型达到 2.7 倍到 3.4 倍。我在 llama.cpp 上单轮编码得到 2.26 倍,所以这取决于任务和引擎,随着引擎更新,可能会很快变得更好。

2. DFlash 2 之上加一个查找表是最佳组合,两个反而更差,这与 DFlash 1 相反。

n-gram 草稿模型会复制上下文中已存在的片段,因此单轮提示是它们的最差情况(上面 +1.2%,中位数实际上是 -2.7%)。重要的是处理代码库的情况,所以我将 18 个固定提示作为一个累积对话驱动:第 1-9 轮逐步为 llama.cpp 构建一个 Gradio 聊天客户端,第 10-18 轮维护它(重新输出文件、文档字符串、重命名、一个 bug、一次重构、测试、README)。

组合--spec-type构建1-9 tok/s相对基线全部18轮接受率(构建)草稿/tok
无推测-65.141.00x56.95--
仅DFlash 2draft-dflash181.892.79x177.5366.4%1.24
DFlash 2 + k4vdraft-dflash,ngram-map-k4v304.924.68x343.5264.2%1.41
DFlash 2 + 两个查找表draft-dflash,ngram-mod,ngram-map-k4v245.843.77x306.0455.6%1.59
DFlash 2 + moddraft-dflash,ngram-mod229.373.52x313.4658.6%1.48
仅查找表,无草稿模型,0显存ngram-mod,ngram-map-k4v133.002.04x170.5459.5%1.00

请看构建列。第 10 轮是“显示完整的最终 app.py”,大约 99% 可草稿化,这会虚高所有推测方法。在全部 18 轮中,k4v 组合读数为 6.03 倍,这是一个关于复制型草稿模型最容易完成任务的真实数字。

我原本预期七月的结果会重现:使用 DFlash 1 时,draft-dflash,ngram-mod,ngram-map-k4v 以 6.01 倍获胜,且 ngram-mod 完成了几乎所有 n-gram 工作。但在 DFlash 2 上,单独使用 k4v 表获胜,单独使用 mod 是最弱的组合,两者一起比单独 k4v 更慢。这可能是草稿 token 数量或早期实现的问题,我们拭目以待。DFlash 1 最多有 15 个草稿槽,DFlash 2 只有 7 个,两个查找草稿模型会互相挤占。

3. 同一行改动给出四个不同答案,其中合成基准是错误的

同一个 DFlash 2 服务器,相同权重,在 --spec-type 后追加 ngram-mod,ngram-map-k4v,再次运行所有测试:

工作负载仅 DFlash 2+ 两个查找表变化
编辑代码,18轮会话,1-9轮181.89245.84+35%
强制长度合成测试,4K入/4K出(中位数)176.57267.82+52%
单轮编码,LiveCodeBench x100153.91155.83+1.2%
撰写全新散文,单次请求158.9111.6-30%

来自 aiperf 的合成基准被其自身的测试框架夸大了。它传递了 ignore_eosmin_tokens,迫使模型越过自然停止点直到循环,而查找草稿模型完美地复制循环。扩展到 36K 时,同一框架显示 DFlash 2 + 查找表为 8.39 倍(498 tok/s)。在 100 个真实提示上,该组合仅值 +1.2%。8.39 倍是那种你可能在某些非常特定用例下获得的数字。

散文是相反的极端:“写一个很长的故事”,上下文中没有可复制的内容,查找表在永远不会命中的猜测上浪费草稿槽,接受率从 54% 降到 32%。那一行是单个插桩请求,是一个探针,不是一次运行。

实际结论:对于迭代式编码和任何重新生成自己上下文的场景,打开查找草稿模型;对于单轮提示和创意写作,关闭它们。它们不消耗显存和预填充,因此这种接受率损失是它们唯一的成本。

4. 推荐的草稿宽度已过峰值,而且 7 本来就是硬上限

每个宽度 16 个编码提示,长度为 8K tokens,来自 livecodebench,cache_prompt false 使每个请求都承担冷预填充:我使用 livecodebench 并将其截取到这个大小来测量最坏情况。

n_maxDFlash 2 tok/s接受率MTP tok/s接受率
2140.5682.6%133.4279.5%
3158.0672.6%154.5377.9%
4174.6072.0%159.0771.7%
5187.1370.4%--
6184.6067.0%154.6462.9%
7168.0659.7%--

运行模型卡推荐的 7 大约留下了 11% 的差距。之前的 8 提示扫描将最佳值定为 6 而不是 5,所以可以说是 5-6;两次扫描都同意 7 已经过了峰值。而且你不能超过 7:草稿 GGUF 携带 dflash.block_size=8,llama.cpp 将 n_draft_max 截断为 block_size - 1,记录警告并使用 7。有些单元格仅基于 16 次有效生成中的 3-7 次(截断的提示有时会让模型立即发出 EOS),因此请将确切峰值视为软性。

该模型上的 MTP 在 n=4 时达到峰值,并在各种上下文下平缓接近 2.5 倍。Qwen 3.8 的 sidecar 声明 nextn_predict_layers=1,即一个训练好的头,而 DFlash 2 读取五个目标层。这是这个 sidecar 的特性,而非 MTP 方法本身的;Qwen 3.6 的 sidecar 有八个头。

5. 长上下文:草稿模型相对更便宜,绝对更昂贵

常见的抱怨是推测解码在长上下文下会崩溃。这句话里隐藏着两种成本。预填充,即草稿模型也必须读取提示,我可以测量。那些深度下的解码我无法测量(见注意事项)。冷预填充,每个深度 12 个提示:

提示深度预填充 tok/s,无预填充 tok/s,DFlash 2速度保持额外等待
1K3,5062,6560.76+0.09 秒
4K3,8453,1640.82+0.23 秒
16K3,6393,1620.87+0.68 秒
64K2,8672,5880.90+2.46 秒
128K2,2392,0560.92+5.20 秒

相对于基线,这种代价随深度而缩小(从 24% 降至 8%)。以秒计则在增加,从 +0.09 秒到 +5.20 秒。两种解读都是正确的;只引用前者是讨好的一面。预填充成本在 1K / 4K / 16K 时分别由 13 / 30 / 69 个输出 token 偿还,因此任何真实答案都能弥补,但使用 128K 提示的人确实要多等五秒才能看到第一个 token。查找草稿模型在这里几乎不花费成本(基线的 0.994-0.997),这也作为对照证明了差距来自草稿模型而非漂移。MTP 的代价更小(1K 时为 0.83,而 DFlash 2 为 0.75)。

在强制长度的合成解码扫描中,DFlash 2 在 512 / 4K / 12K / 36K 下从 1.59 倍提升到 2.62 倍、2.96 倍、3.55 倍,而基线从 67.6 降到 59.3 tok/s。DFlash 1 在自身最大值 15 时,七月在同一框架上 36K 下为 4.44 倍(本月重新测量时更高),在匹配宽度 7 下为 3.71 倍。我原本期望新草稿模型在所有地方都胜出。但它没有:它在相同宽度下真实提示上胜出,但在合成长上下文竞赛中输给了拥有更多槽位的旧草稿模型,因为它被限制在 7。

6. --spec-draft-p-min 在 DFlash 2 上是无效操作,在 MTP 上也没有收益

自适应草稿截断应能让草稿模型在不确定时提前停止一个块以节省资源。还有来自 DeepSeek Dspark 论文的更先进方法,但它们刚刚落地到 vLLM。我记录了草稿宽度和每秒周期数,而不仅是 tok/s:

草稿模型p_mintok/s接受率草稿宽度周期/秒
DFlash 20.00195.371.6%6.99832.51
DFlash 20.85171.160.5%6.99832.69
MTP0.00161.790.4%3.00143.54
MTP0.85155.797.6%2.64243.52

在 DFlash 2 上,草稿宽度在 0.00 和 0.85 时完全相同。common/speculative.cpp 中有四个草稿实现:draft_simpledraft_eagle3、DFlash 1 分支和 draft_mtp 都尊重 p_min;而 is_dflash2 选择器分支从不查询它,因为它读取的是选择器格,而不是概率。服务器仍然在启动横幅中打印该标志,因此基于日志的检查会通过,但实际上什么也没发生。那一行 12.4% 的吞吐量下降来自文本,而非标志:服务器做了相同的工作(周期/秒在 1.5% 以内),只是采样输出对相同的七个 token 接受了更少。我差点发表“p_min 造成 12% 开销”。

在 MTP 上,该标志完全按文档工作(宽度 3.00 → 2.64,接受率 90% → 98%),但吞吐量没有变化,相对于 4.1% 的噪声底,最多 +0.8%。

我会运行的配置

* 迭代式编码、代理、任何重新生成自身上下文的场景:--spec-type draft-dflash,ngram-map-k4v --spec-draft-n-max 5
* 单轮提示和问答:--spec-type draft-dflash --spec-draft-n-max 5
* 散文:单独使用 DFlash 2,不使用查找表。
* 暂时将 KV 缓存保持在 f16,或者测试一下,它可能很快会稳定,但上一个版本有问题,我使用默认值。
* 忽略 --spec-draft-p-min

git clone https://github.com/ggml-org/llama.cpp.git
cd llama.cpp
git fetch origin pull/27342/head:pr-27342
git switch pr-27342

# NVIDIA CUDA
cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_CUDA=ON
cmake --build build -j

# Apple Silicon
cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_METAL=ON
cmake --build build -j

# Best measured config: iterative coding, agents, anything that re-emits its own context
# (4.68x on the multi-turn coding session vs 2.79x for DFlash 2 alone)
./build/bin/llama-server \
  -hf ggml-org/Qwen3.8-27B-GGUF:Q4_K_M \
  -hfd incoai/Qwen3.8-27B-DFlash2-GGUF:Q4_K_M \
  --spec-type draft-dflash,ngram-map-k4v \
  --spec-draft-n-max 5 \
  -ngl -1 --spec-draft-ngl all \
  -fa on \
  -c 262144 \
  --parallel 1 \
  --jinja --reasoning off \
  --no-mmproj \
  --host 0.0.0.0 --port 8000 \
  --alias qwen38-dflash2-k4v

所有注意事项

* 这次没有准确度测量。贪心推测解码在构造上是输出无损的,七月份的研究测量过(MATH-500:100 题中 87 对 86,然后 500 题中 440 对 435),但那是 Qwen 3.6 搭配 DFlash 1,我这次没有重测。只有一个 LiveCodeBench 冒烟测试。
* 没有深上下文解码。在 64K 和 128K 下,每次生成返回一个 token 就停止了,所以那些深度下预填充是有效的,但解码不存在。
* 只有一个实验被重复过(p_min 对照)。其他所有都是一次样本。那些重复的差异,4.1% 和 14.7%,是整篇文章的噪声底。
* 多轮测试框架可能测得错误的东西。它没有声明工具,但在默认采样下,模型有时会用 <tool_call> 块回答并等待一个永不出现的结果,而那个会话因为工具调用 XML 可预测而变快。有一次运行正是这样(1,134 个 token,而同类运行产生了 50K-73K),被 token 数发现并重新运行。
* 这是单台机器上、并发 1 下的一个工作负载族(编码)。96 GB 显卡不是大多数人会用的。草稿模型只有 1.1 GB,KV 数学中没有任何东西依赖显卡,所以我预计在 24-32 GB 显卡和更小上下文下形态会保持,但我没有测量过。
* DFlash 2 是一个 PR 构建。合并后数字可能会变化。

资源

* 仓库(两项研究,本项在顶部):[https://github.com/lukaLLM/DFlash2\_Qwen3.8\_3.6\_27B\_LlamaCPP56
* 视频讲解(前半部分解释机制、路径选择器和卷积,其余部分更深入分数等):[https://youtu.be/RBlRTUwJMI457
* 一键设置,构建 PR 镜像,下载模型,冒烟测试并运行服务器:./scripts/setup_dflash2.sh --arm dflash2_ngram(arms: base, dflash2, mtp, ngram, dflash2_ngram)。Compose 文件 docker/docker-compose-qwen38-dflash2.yaml;使用 LLAMA_SPEC_TYPE=...LLAMA_SPEC_N=5 进行消融。
* 按顺序复现整个研究:./scripts/run_all_benchmarks.sh,然后 run_matched_n.shrun_context_scaling.shrun_bench_ngram.shrun_nmax_redo.shrun_pmin_agentic.sh
每个数字都在一个机器可读文件中:benchmark/results_summary.csv(TABLE 8-14 是本项研究)。原始产物在 artifacts/q38_/ 下,热日志在 artifacts/thermal/,被隔离的运行及其原因在 artifacts/_suspect/README.md
* 带图表的详细报告:仓库中的 report/dflash2-report.html
* 博客:[https://inco.ai/blog/dflash2/58
* 此前帖子:七月的 DFlash 1 [https://www.reddit.com/r/LocalLLaMA/comments/1uq0h4o/i\_tested\_freshly\_merged\_dflash\_in\_llamacpp\_on/59 和 n-gram 组合 [https://youtu.be/zNUoHONUHGk60

AI 被滥用编辑了这篇文章。

问题:

* 有没有人在并发 1 下用这个模型在 SGLang 或 vLLM 上运行过 DFlash 2?我想知道 2.7-3.4 倍的说法在那里是否成立,以及与我 2.26 倍之间的差距有多少来自引擎。
* 有 4090 或 5090 且显存预算 24-32 GB 的朋友吗:n=5 对你们来说仍然比 7 好吗?k4v 单独组合在你们自己的多轮编码中的表现如何?
* 有没有人尝试过我没有想到的其他组合?

阅读原文
📚 相关主题 大语言模型推理优化

订阅 AI Pulse

每天 08:00 · 12:30 · 18:30 · 23:50 更新