2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

# Qwen3.5-4B + D-Flash を 12GB GPU で動かし更に高速化を試す(日本語環境での実測)

2
Last updated at Posted at 2026-05-24

はじめに

ここまでの2記事では、Qwen3.5-2B 内蔵の MTP で遊んできました。「n=1 だけ有効で +15〜19%」という控えめだけど着実な結果です。

ただ、世の中には「いや 2B じゃちょっと…」というケースもあります。コード生成とか長文要約とかになると、できれば 4B / 8B を使いたい。

そして 4B クラスになると、投機的デコーディングの恩恵もまた違う形で出てくるはず。今回紹介する D-Flash は、前回までの MTP とはまったく別方式の drafter で、論文 (z-lab, 2026) では EAGLE-3 比 2.5x、Qwen3-8B で最大 6x の lossless speedup と書かれています。なかなか強気な数字です。

「12GB の RTX 4070 で 4B + D-Flash って入るのか?」「公称通り速くなるのか?」というのが本記事のテーマです。

例によって先に結論を書きます。

  • FP8 baseline 60 tok/s → D-Flash で 71 tok/s (+18%)
  • 同条件の MTP n=3 も 70.6 tok/s (+18%) で、ほぼ同速
  • 12GB GPU では vLLM のスケジューラ制約が効いて D-Flash の並列性が発揮されない
  • 4B 自体が 12GB では ~60 tok/s の壁 があり、2B+MTP (147 tok/s) には届かない

「D-Flash すごい」という記事を書こうとしたら、12GB GPU での現実の壁にぶつかった、という話です。

D-Flash とは何か

D-Flash は z-lab がリリースしている block diffusion drafter です。論文は "DFlash: Block Diffusion for Flash Speculative Decoding" (Chen et al., 2026)。

通常の draft モデル方式 (たとえば EAGLE-3) は、draft 側でも autoregressive に 1 token ずつ生成します。先読み数を増やすと draft 側の forward 回数が線形に増えるので、draft latency を抑えるためにすごく浅い draft モデル (1 層 Transformer など) を使わざるを得ません。

D-Flash はここを block diffusion で並列化します。

  1. target モデルの中間層から特徴量を抽出
  2. 軽量な projection を通して draft 層の KV cache に注入
  3. draft は注入された KV を見ながら、block size 分の token を 1 回の forward で一気に生成

block size 16 で動かすと、1 step で 16 token がほぼ並列に出てきます。autoregressive draft より深いネットワークを使えるので、受理率を犠牲にせずに draft latency を抑えられる、という設計です。

公式 benchmark では Qwen3-8B で最大 6x、4B で HumanEval 3.7x の lossless speedup を主張しています。ただこれらは十分な VRAM がある環境での数字です。12GB GPU での話は後述します。

ちなみに draft モデルは target の embedding と LM head を再利用するので、parameter 増加は最小限です。今回の 4B 用 drafter z-lab/Qwen3.5-4B-DFlash は BF16 で約 1.0 GiB でした。

vLLM での指定方法

vLLM では --speculative-config で投機的デコーディング方式を指定します。

D-Flash は外部 drafter を必要とするので、model フィールドに drafter モデルを書きます。

vllm serve RedHatAI/Qwen3.5-4B-FP8-dynamic \
  --dtype auto \
  --speculative-config '{"method": "dflash", "model": "z-lab/Qwen3.5-4B-DFlash", "num_speculative_tokens": 15}' \
  --attention-backend flash_attn \
  --max-num-batched-tokens 4096 \
  --max-model-len 2048 \
  --gpu-memory-utilization 0.937 \
  --enforce-eager

--attention-backend flash_attn は D-Flash 公式手順で必須です。num_speculative_tokens: 15 は block size 16 に対応する公式推奨値 (block 内 1 token は前処理に使われる扱い)。

比較する MTP は前記事と同じく qwen3_next_mtp、n=3 で測ります。

12GB GPU の VRAM 事情

Qwen3.5-4B FP8 を 12GB GPU で動かすのは、率直に言ってかなり厳しいです。

理由は Qwen3.5 のアーキテクチャ側に2つあります。

1. Vision Encoder が BF16 のまま乗る

Qwen3.5 はネイティブマルチモーダルで、全サイズに Vision Encoder が含まれます。FP8 量子化は LLM 本体だけが対象で、Vision Encoder は BF16 のまま ロードされます。今回の構成では Vision Encoder だけで約 3.0 GiB です。

2. Mamba の SSM state が固定サイズ

Qwen3.5 は Gated DeltaNet + Mamba のハイブリッドアーキです。Mamba の再帰状態 (SSM state) は max-model-len に関係なくほぼ固定サイズ (~0.9 GiB) を占有します。普通の Transformer は seq_len を下げれば KV cache も小さくなりますが、ここは下がりません。

VRAM 内訳をまとめるとこうなります (vLLM 起動ログから推定)。

項目 サイズ
LLM 本体 (FP8) 約 4.0 GiB
Vision Encoder (BF16) 約 3.0 GiB
D-Flash drafter (BF16) 約 1.0 GiB
KV cache 約 1.3 GiB
activation バッファ 約 1.0 GiB
合計 約 10.3 GiB

RTX 4070 (11.59 GiB) のうち、デスクトップ環境などの常時消費分を差し引くと実用可能なのは約 11.0 GiB。本当にギリギリです。

CUDA graph を有効にすると graph capture に追加で 1 GiB 以上必要になり、簡単に OOM します。そのため今回は --enforce-eager で CUDA graph を無効化 した状態で全構成を測っています。

結果

計測条件は前記事までと同じで Medium (出力 256 tokens) と Long (出力 512 tokens)。warmup 1 回 + 本計測 5 回の中央値です。

Medium

構成 tok/s baseline 比 acceptance
Base-FP8 59.8 1.00 -
MTP-FP8-n3 70.3 1.18 0.33
DFlash-FP8-n15 62.8 1.05 0.04

Long

構成 tok/s baseline 比 acceptance
Base-FP8 60.0 1.00 -
DFlash-FP8-n15 71.0 1.18 0.054
MTP-FP8-n3 70.6 1.18 0.33

D-Flash と MTP がほぼ同速 という結果になりました。Medium では MTP が上回り、Long ではほぼ同等。受理率は D-Flash が 0.04〜0.05 と非常に低く、MTP は 0.33 と上回っています。

論文で主張していた 2x 以上の speedup には遠く届かない結果です。理由は次節に書きます。

vLLM のスケジューラ制約

D-Flash の結果がふるわなかった最大の要因は vLLM のスケジューラが num_speculative_tokens=15 に対してバッチサイズを自動で絞る 動作です。

起動ログに以下の警告が出ています。

WARNING: max_num_scheduled_tokens is set to 512 based on the speculative decoding settings.
This may lead to suboptimal performance. Consider increasing max_num_batched_tokens.

計算式は:

max_num_scheduled_tokens = max_num_batched_tokens / (num_speculative_tokens + 1)
                         = 4096 / (15 + 1) = 256 → 512 に切り上げ

max_num_batched_tokens=4096 でも、D-Flash n=15 では実効的に 512 tokens/step に絞られます。このスロットリングによって D-Flash が本来持つ「1 step 16 token 並列」という並列性が発揮できていません。

解決するには max_num_batched_tokens4096 × 16 = 65536 相当まで増やす必要がありますが、VRAM が足りません。12GB GPU では D-Flash n=15 の本来の性能を引き出すのは構造的に困難という結論です。

MTP の起動ログには同じ警告は出ておらず、MTP は n=3 でもスロットリングを受けていません。これが Medium で MTP が D-Flash を上回った理由です。

出力サンプル

数値だけでは性能感が掴みづらいので、実際の出力も見比べてみます。プロンプトは Long 条件 (地方自治体の生成 AI 導入ガイド) を使いました。

今回は enable_thinking: false を指定して計測しているため、全構成で最初から日本語の回答本文が出力されます。

[DFlash-FP8-n15 Long の出力冒頭]
# 地方自治体が生成 AI を導入する際の進め方:実務者向けガイド

地方自治体は、近年「DX(デジタルトランスフォーメーション)」の推進により、
業務効率化や住民サービスの向上を急務としています。その中で、生成 AI(Generative AI)は、
公文書の作成支援、市民相談の自動化、政策立案のシミュレーションなど、
多岐にわたる課題解決の可能性を秘めています。

## 1. 導入目的の整理

まず「なぜ AI を導入するか」を明確に定義することが不可欠です。目的が曖昧だと、
後々の評価や予算承認に支障をきたします。

具体例:市民相談の 30% を AI による自動回答で対応し、窓口の待ち時間を 20% 削減する。
[MTP-FP8-n3 Long の出力冒頭]
# 地方自治体が生成 AI を導入する際の進め方:実務者向けガイド

地方自治体は、近年「DX(デジタルトランスフォーメーション)」の推進により、
業務効率化や住民サービスの向上を目的として IT 投資を加速させています。

## 1. 導入目的の整理

生成 AI を導入する前に、単なる「流行り」ではなく、自治体の抱える具体的な課題と、
AI が解決できる範囲を明確に定義する必要があります。

具体例:文書作成業務の効率化 ─ 月次報告書ドラフト作成を自動化し、
職員の作業時間を 50% 削減する。

両構成とも実用的な日本語が出ています。速度は同じでも出力の細部はわずかに違います (どちらが「正解」とは言えない)。

受理率と速度の関係

今回の受理率を整理すると:

  • D-Flash: Medium 0.04 / Long 0.054 (5% 未満)
  • MTP: Medium 0.33 / Long 0.33

一見 MTP のほうが圧倒的に受理率が高いのに速度が同等なのは、D-Flash が「受理率が低くても1回の forward で多数の token を扱う」設計だからです。受理率 0.05 でも 15 draft token に対して 0.75 token 回収しているのは MTP n=3 (0.33 × 3 ≈ 1.0 token) と同水準です。

ただ前述のスロットリングが効いているため、「1 forward で 15 token 並列」という設計の本領が出せていない状態です。

12GB GPU での教訓

  • 4B 系は --enforce-eager を前提に設計 する。CUDA graph に必要な 1 GiB を諦めるしかない
  • --gpu-memory-utilization0.937 が実質上限
  • --max-model-len を下げても Mamba SSM state は減らない ので KV cache の節約はほぼ無理
  • D-Flash n=15 は max_num_batched_tokens=4096 では スロットリングを受けて本来の性能が出ない
  • 12GB GPU で D-Flash の公称性能を出すには、より大きな max_num_batched_tokens が必要 → VRAM 不足で困難

まとめ

  • D-Flash は block diffusion を draft に使う方式 で、draft 側を並列化して draft latency を抑える
  • 12GB GPU (RTX 4070) では vLLM スケジューラのスロットリングが効き、D-Flash n=15 は Medium +5% / Long +18% にとどまった
  • 同条件の MTP n=3 も +18% で、今回の環境では D-Flash と MTP がほぼ同速
  • 4B + D-Flash / MTP のどちらも ~70 tok/s で、2B + MTP-n1 (147 tok/s) の半分以下

12GB GPU での D-Flash は「動く」が「公称通りの高速化は出ない」というのが正直な評価です。

全体を通しての所感

シリーズ3本で Qwen3.5-2B + MTP / Qwen3.5-4B + D-Flash を試してきました。

結果を並べると:

サイズ 構成 Long tok/s
2B Base-FP8 124
2B MTP-FP8-n1 147 (+19%)
4B Base-FP8 60
4B DFlash-FP8-n15 71 (+18%)
4B MTP-FP8-n3 71 (+18%)

12GB GPU の現実として、4B + 投機デコーディングでも ~70 tok/s が上限 になっています。Vision Encoder + Mamba state + drafter でほぼ VRAM が埋まり、スケジューラも制限を受けます。

対して 2B + MTP-n1 (147 tok/s) は 4B 系の2倍以上速い。「4B の品質がほしいなら 4B を使う、速度がほしいなら 2B + MTP」という棲み分けになります。

「4B + D-Flash で 2B より速くする」という使い方は、VRAM 余裕のある 16〜24GB GPU なら現実的かもしれません。12GB では CUDA graph も使えず、スケジューラも絞られるので、本来の D-Flash の性能評価としてはフェアな条件ではないと思います。

Speculative decoding は「モデルをそのままに decode を速くする」という方向でちゃんと機能します。ただし GPU の VRAM とスケジューラの制約が強く絡むので、「環境込みで実測する」のが大事だと改めて感じました。新しいモデルを触るときに「投機ヘッドはあるか」「drafter は出ているか」が自然にチェック項目になりそうです。

補足: 「以前の計測では 2x 以上出ていたのでは?」

筆者自身の事前計測では「Qwen3.5-4B FP8 + D-Flash n=15 で 2.4〜2.6x」という数字が出ていました。本記事の 1.18x との落差が大きいため、何が変わったのかを整理しておきます。

旧計測値と今回の比較

Long tok/s baseline 比 acceptance
旧計測 (thinking ON) 156.8 2.59x 0.204
今回 (thinking OFF) 71.0 1.18x 0.054

tok/s が半分以下、acceptance rate も 1/4 以下になっています。

原因1: thinking モードが acceptance rate を水増ししていた

最大の原因はここです。

Qwen3.5 は thinking モードがデフォルト ON で、vLLM の /v1/chat/completionschat_template_kwargs: {enable_thinking: false} を明示しないと <think>...</think> ブロックが挿入されます。

thinking 区間の出力は、こんな構造になります。

<think>
まず問題を整理します。地方自治体が生成 AI を…
次に考慮すべきは…ということになります。
したがって、以下のような手順が適切です…
</think>

この区間のトークン列は、

  • 「まず」「次に」「つまり」「したがって」という接続詞の反復
  • 直前の問いかけ文字列を言い換えるパターン
  • 自己参照構造 (「この問題は X → X の解決には Y → Y とは…」)

という特性があり、統計的に予測しやすいです。Speculative decoding のドラフターは「次トークンの確率分布がシャープなほど受理率が上がる」ので、thinking 区間では acceptance rate が実際の回答トークンよりも大幅に高くなります。

旧計測では 512 token の出力のうち、かなりの割合が thinking 区間だった可能性が高く、その部分で acceptance rate が嵩上げされていたと考えられます。シリーズ前2記事の 2B + MTP でも同じ構造で旧値と新値にギャップがあり、「2B MTP-FP8 n=3 Long が 195 tok/s → 131 tok/s」という変化がまさにこれです。

原因2: vLLM nightly のスケジューラ制約

本文でも触れた max_num_scheduled_tokens の自動スロットリングです。

max_num_scheduled_tokens = max_num_batched_tokens / (num_speculative_tokens + 1)
                         = 4096 / (15 + 1) = 256 → 512 に切り上げ

旧計測時のバージョンではこの制約がなかった可能性があります。「同じ設定でも vLLM のバージョンが違うと結果が変わる」典型例です。具体的にどのコミットで入ったかは changelog では追い切れていませんが、今回使用の vllm/vllm-openai:nightly (v0.21.1rc1.dev243) では起動時に明確な WARNING として出ています。

D-Flash ベンチを読むときのチェックポイント

投機的デコーディングの計測値を比較するときは、以下を必ず確認してください。

確認項目 今回の設定
enable_thinking false (明示)
vLLM バージョン nightly v0.21.1rc1.dev243
max_num_batched_tokens 4096
num_speculative_tokens 15
concurrency 1 (リクエスト逐次)
出力 token 数 256 / 512

これらが揃わないと、同じモデル・同じ method でも tok/s が 2x 以上変わることがあります。特に thinking ON/OFFvLLM バージョン の 2 点は影響が大きく、数字だけ引用するときに見落としがちです。

参考リンク

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?