ローカルLLM 実行計画の実測(第10章)で、Qwen3-4B は 56.5 tok/s に到達した。 これはGPUのメモリ帯域が決める上限に近い。帯域を増やさずに、さらに速くする手段はあるか—— その問いに対する候補が Speculative Decoding である。本書はその成立可能性を検討し、実測した。
npaka 氏の記事「Qwen3.8-27Bにおすすめの推論エンジン」(2026-08-23)で、 Speculative Decoding による高速化が目玉として紹介されていた。
| 手法 | 環境 | 効果 |
|---|---|---|
| mlx-dspark | M4 Pro / 8bit | 8.3 → 20.3 tok/s(2.45倍) |
| DFlash 2 | H200 / SGLang | GSM8K 3.43倍、MATH-500 3.34倍 |
ただし記事が扱っていた実装はいずれも本PCでは使えない。 mlx-dspark は Apple Silicon 専用、DFlash 2 は SGLang(NVIDIA の大容量GPU向け)専用である。 記事の想定環境も最小で VRAM 24GB と、本PCの6倍以上を前提にしている。
一方で、Speculative Decoding という手法自体は llama.cpp にも実装がある。 そこだけを取り出して本PCに適用できないかを調べたのが本書である。
| 論点 | 結果 |
|---|---|
| VRAMに収まるか | ぎりぎりQwen3-4B + 0.6B ドラフトで 3925 MiB(4096 MiB 中)。KV を q8_0 にすると 3515 MiB |
| 速くなるか | 遅くなる単体の 57 tok/s に対し、どの構成も 29〜47 tok/s |
| 判断 | 見送る第5章の判断基準の「56 tok/s 未満」に当たる |
記事の 2.45 倍・3.43 倍という数字は、大きなモデルを潤沢なメモリで動かす環境のものである。 本PCの本命は 2.32 GiB と軽く、ドラフトの実行時間を取り返せなかった(第5章の結果)。
LLM は1トークンを作るたびにモデル全体の重みをメモリから読み出す。 Qwen3-4B なら 2.32 GiB を毎回読む。計算そのものは軽く、待ち時間の大半はメモリ転送である。 これが「生成速度はメモリ帯域で決まる」と言われる理由で、実測の 56 tok/s もこの上限に近い。
出力は通常の生成と完全に一致する。品質を落として速くする手法ではなく、 「本命モデルが出すはずの答え」を先回りして当てにいく仕組みである。外れても正しさは損なわれず、単に速くならないだけ。
| オプション | 意味 |
|---|---|
-md / --model-draft | ドラフトモデルの GGUF を指定する |
--spec-type draft-simple | ドラフトモデルで先読みする。b10612 では既定が none で、-md だけではドラフトを読み込んでも使わない |
-ngld / --n-gpu-layers-draft | ドラフトモデルをGPUに載せる層数。ここを削ると本命に回せる |
--spec-draft-n-max | 一度に先読みするトークン数の上限(既定 3)。大きいほど当たった時の利得が大きく、外れた時の無駄も大きい。b10612 では --draft-max は廃止された |
--spec-draft-n-min | 先読みの下限。--draft-min は廃止された |
--draft-p-min | ドラフトの確信度がこの値を下回ったら先読みを打ち切る |
llama-server.exe ^
-m "C:\AI-models\qwen\Qwen3-4B-Instruct-2507-GGUF\Qwen3-4B-Instruct-2507-Q4_K_M.gguf" ^
-md "C:\AI-models\qwen\Qwen3-0.6B-GGUF\Qwen3-0.6B-Q4_K_M.gguf" ^
-ngl 99 ^
-ngld 99 ^
--spec-type draft-simple ^
--spec-draft-n-max 4 ^
-c 4096 ^
--host 127.0.0.1 --port 8080
| 条件 | 内容 | 本PCでの充足 |
|---|---|---|
| 語彙の一致 | ドラフトと本命のトークナイザが互換である必要がある。異なる系列のモデルは組み合わせられない | 満たせるQwen3 系で揃える |
| 両方をメモリに載せる | ドラフトと本命の両方が同時に必要。KVキャッシュも2つ分要る | ぎりぎり第4章で見積る |
| ドラフトが十分速い | 先読みが遅いと、検証を減らした利得を食い潰す | 満たせる0.6B は 4B の4倍近く軽い |
| 受理率が高い | ドラフトの推測が当たらないと、単に無駄な計算が増える | 未知実測でしか分からない |
計画書 第10章の実測どおり、本PCの VRAM は 4095 MiB のうち 808 MiB が画面表示等で先に使われており、空きは 3287 MiB(3.21 GiB)。 ここに2つのモデルを同居させることになる。
| 項目 | 見積り | 根拠 |
|---|---|---|
| 本命 Qwen3-4B Q4_K_M | 2376 MiB | 計画書 第10章の実測値(2.32 GiB) |
| ドラフト 0.6B Q4_K_M | 約 410 MiB | 推定。パラメータ数からの概算 |
| KVキャッシュ ×2 | 約 300 MiB | 推定。-c 4096 相当を2モデル分 |
| 合計 | 約 3086 MiB | 空き 3287 MiB に対して余裕 約 200 MiB |
計算上は収まる。ただし残り 200 MiB は誤差で消える水準である。 計画書 第10章で確認したとおり、VRAM を超えてもエラーにはならず 共有メモリに逃げて数十倍遅くなるため、超過に気づきにくい点にも注意が要る。
| 手 | 効果 | 副作用 |
|---|---|---|
-ngld を下げる | ドラフトの一部をCPUに置き、VRAMを空ける | 先読みが遅くなり、利得が減る |
| KV量子化 | --cache-type-k q8_0 でKVを半分に | ほぼ無し。計画書でも採用済み |
| より小さいドラフト | 0.6B より小さい版があれば | 受理率が下がる恐れ |
| 画面表示を内蔵GPUへ | 808 MiB のうち数百MBを取り戻せる | 設定変更が要る |
| 項目 | 内容 | 取得量 |
|---|---|---|
| ドラフトモデル | Qwen3-0.6B の GGUF(Q4_K_M)。本命と同系列で語彙を揃える | 約 0.4 GB |
| 対照データ | 計画書 第10章の Qwen3-4B 単体 56.49 tok/s | 取得済み |
| No | 構成 | draft-max | 狙い |
|---|---|---|---|
| 1 | Qwen3-4B 単体(対照) | — | 基準。56.49 tok/s と一致するか確認する |
| 2 | 4B + 0.6B ドラフト | 2 | 控えめな先読み。外れた時の損失が小さい |
| 3 | 4B + 0.6B ドラフト | 4 | 標準的な設定 |
| 4 | 4B + 0.6B ドラフト | 8 | 強気の先読み。受理率が高ければ最も伸びる |
| 5 | 4 に KV量子化を追加 | 8 | VRAM を空けて余裕を作る |
nvidia-smi で 3287 MiB を超えていないか。超えていれば速度低下の原因はここ--draft-max を下げる| 結果 | 次の一手 |
|---|---|
| 70 tok/s 以上 | 採用する。計画書の常用構成を差し替え、起動スクリプトを更新する |
| 56〜70 tok/s | 効果はあるが VRAM の余裕を失う。コンテキスト長とのトレードオフで判断する |
| 56 tok/s 未満 | 見送る。帯域律速という第10章の見立てが裏付けられたことを記録して終える |
RAM 32GB・画面表示は内蔵GPU の構成で測った(2026-09-30)。ドラフトは unsloth/Qwen3-0.6B-GGUF の Q4_K_M(396,705,472 B)。
-c 4096 -np 1 -ngl 99 -ngld 99 --spec-type draft-simple で立て、2 つの問を温度 0・seed 1 で 256 トークンずつ出させた。
1 回目で温め、2 回目を記録した。測ったのは tools\50_run\bench-spec.cmd。
| No | 構成 | 文章 tok/s | 受理率 | コード tok/s | 受理率 | VRAM |
|---|---|---|---|---|---|---|
| 1 | Qwen3-4B 単体(対照) | 57.15 | — | 57.00 | — | 3129 MiB |
| 2 | +ドラフト n-max 2 | 42.34 | 52% | 47.29 | 67% | 3925 MiB |
| 3 | +ドラフト n-max 4 | 36.31 | 39% | 43.55 | 52% | 3925 MiB |
| 4 | +ドラフト n-max 8 | 29.32 | 20% | 44.99 | 38% | 3925 MiB |
| 5 | 4 +KV q8_0 | 33.75 | 23% | 47.27 | 38% | 3515 MiB |
受理率は、ドラフトが出したトークンのうち本命が採った割合(timings.draft_n_accepted ÷ draft_n)。
出力を保存して比べ、単体でも一度にまとめて計算する形を変えて同じことが起きるかを見た(bench-spec.cmd -Only の No6〜9)。
| 構成 | 文章 | コード |
|---|---|---|
| 単体を立て直して 2 回目 | 一致 | 一致 |
| 単体 2 本同時(np 2) | 一致 | 一致 |
| 単体 3 本同時(np 3) | 一致 | 一致 |
| 単体 5 本同時(np 5) | 不一致先頭から | 不一致439 文字目から |
| ドラフト n-max 2・4 | 不一致224 文字目から | 不一致439 文字目から |
"Path '$Path' does not exist or is not accessible.")にずれた3 本・5 本同時は 1 スロット 4096 トークンを保つため -c を本数倍にしており、VRAM があふれて速度は 10〜21 tok/s に落ちた。出力の比較には影響しない。
--model-draft 系オプションの仕様。版によって名称が変わるため、
使用中のビルド(b10612)の --help を正とする