分析

Gemma 4 MTP の GB10 実機ベンチ — 推論を 26B で 1.83 倍、31B Dense で 3.52 倍に高速化

コード生成で 3.52 倍、自然言語 (英語・日本語) で約 2.5 倍 — 31B Dense + 1 GPU での独立計測 (Google 公式『最大 3 倍』を上回る)

結論サマリ

NVIDIA GB10 (GX10、121.6GB unified LPDDR5X、273 GB/s) で Google の Gemma 4 MTP (Multi-Token Prediction) drafter (本モデルの予測を補助する小型の予測モデル) を実機検証しました。3 種類の workload (どのような出力を生成するか) すべてで MTP の効果が確認でき、特に 31B Dense では Python コード生成で推論を 3.52 倍に高速化しました。これは Google 公式ブログ の「最大 3 倍 faster」(no quality loss) という主張に対し、独立した 1 GPU 環境で同等以上の数字を得たことになります。

Category26B-A4B + MTP+patch31B Dense + MTP+patch
日本語金融分析1.20 倍 (16.47s → 13.74s)2.54 倍 (91.95s → 36.23s)
英語マクロ分析1.36 倍 (15.15s → 11.14s)2.52 倍 (91.41s → 36.22s)
Python コード生成1.83 倍 (15.17s → 8.30s)3.52 倍 (91.41s → 26.00s)

26B (水色) と 31B (緑) を並べて視覚化すると、特に Python では 31B の伸びが顕著です。

workload 別 高速化倍率 (baseline = 1.00 倍、K=4)
日本語金融分析
26B+MTP
1.20 倍
31B+MTP
2.54 倍
英語マクロ分析
26B+MTP
1.36 倍
31B+MTP
2.52 倍
Python コード生成
26B+MTP
1.83 倍
31B+MTP
3.52 倍
バー長は 3.52 倍を 100% としたスケール。31B (緑) は全 workload で 26B (水色) を大きく上回る。

検索範囲では、31B Dense + 1 GPU + MTP の独立ベンチマーク (公式以外の第三者計測) は公開ブログ・GitHub Issue ともに見当たらず、本検証がそうした事例として最初のものになります。なお、検証で使った vLLM image (公開直後) はそのままでは acceptance rate (採択率: drafter の提案のうち target モデルが受理した割合) が 0% で動かず、PR #41745 の head commit を bind mount (ホスト側のファイルでコンテナ内のファイルを上書きマウントする手法) で当てる必要がありました。詳細はセクション 5 で扱います。


1. 検証環境

項目
ハードウェアNVIDIA GB10 (GX10 内蔵 SoC、ARM aarch64、SM 12.1)
メモリLPDDR5X 128GB unified (CPU/GPU 共有、cache coherent)
メモリ帯域273 GB/s
target モデル (本モデル)RedHatAI/gemma-4-26B-A4B-it-FP8-Dynamic (26B-A4B MoE) / RedHatAI/gemma-4-31B-it-FP8-Dynamic (31B Dense)
drafter (補助モデル)google/gemma-4-26B-A4B-it-assistant (832MB) / google/gemma-4-31B-it-assistant (832MB)
vLLM imagevllm/vllm-openai:gemma4-0505-arm64-cu130 (Google MTP 公開と同日 push)
patchPR #41745 の head commit d8b3826 を bind mount
設定num_speculative_tokens=4 (= K、1 step で提案する token 数)、kv-cache-dtype fp8max-model-len 4096、temperature=0.7

「本番」と書いた場合は、筆者が日次運用しているニュース要約 cron 用の vLLM サーバー (毎日 02:00 JST、31B-FP8-block) を指します。


2. 詳細結果

2.1 workload 別の高速化倍率 (3 prompts × 3 runs)

3 種類のプロンプトを num_speculative_tokens=4 固定で計測しました。プロンプト全文はセクション 4.5 (検証で使ったプロンプト全文) に掲載しています。

26B-A4B + MTP+patch

Categorybaseline (Dynamic、MTP なし)+ MTP+patch (K=4)Speedup
ja_finance16.47s, 60.2 ch/s, 992 chars13.74s, 73.6 ch/s, 1011 chars1.20 倍
en_finance15.15s, 170.1 ch/s, 2576 chars11.14s, 232.7 ch/s, 2592 chars1.36 倍
py_code15.17s, 148.9 ch/s, 2260 chars8.30s, 279.8 ch/s, 2321 chars1.83 倍

31B Dense + MTP+patch

Categorybaseline (Dynamic、MTP なし)+ MTP+patch (K=4)Speedup
ja_finance91.95s, 10.5 ch/s, 967 chars36.23s, 26.3 ch/s, 952 chars2.54 倍
en_finance91.41s, 27.9 ch/s, 2551 chars36.22s, 71.0 ch/s, 2572 chars2.52 倍
py_code91.41s, 24.9 ch/s, 2277 chars26.00s, 86.7 ch/s, 2254 chars3.52 倍

「コード生成 > 英語 > 日本語」の順に高速化倍率が伸びるのは、drafter の予測難度に依存するためです。

  • Python: def importpd.DataFrame( などが続きやすく drafter が予測を当てやすい (= acceptance が高い)
  • 英語: 自然言語ではあるものの語順や構造が単調 (= 中程度)
  • 日本語: 語順の自由度が高く、文字あたりの token 数も多い (CJK は 1 token あたり 1.5 〜 2 文字) — drafter が当てにくく、表示文字数も稼ぎにくい

2.2 per-position acceptance — 31B drafter は全 position で 26B drafter を上回る

K=4 における per-position acceptance (各位置での採択率) を比較すると、31B drafter が全 position で 26B drafter を上回っていることがわかります。

per-position acceptance rate (K=4)
pos 0 (1 番目)
26B
72.2%
31B
78.7%
pos 1 (2 番目)
26B
53.6%
31B
58.9%
pos 2 (3 番目)
26B
41.2%
31B
45.6%
pos 3 (4 番目)
26B
30.9%
31B
34.6%

差は pos 0 で +6.5 ポイント、pos 1 で +5.3 ポイント、pos 2 で +4.4 ポイント、pos 3 で +3.7 ポイントです。深いポジションほど差は縮まりますが、平均すると約 +5 ポイント。31B 用 drafter は 31B target と同じ 32 layer / 隠れ次元構造を共有しているため、predict-then-verify (drafter で予測してから target で検証する) における hidden state alignment (隠れ状態の整合性) が良くなる、と解釈できます。

2.3 26B vs 31B の 4 構成直接比較 (Python yfinance タスク)

絶対速度 (chars/s) で並べると、26B + MTP+patch が圧倒的に速いことが視覚化できます。

構成secchars/s26B baseline 比用途
26B baseline15.17s148.91.00 倍(基準)
26B + MTP+patch8.30s279.81.83 倍 ← 最速速度重視、MoE 活用
31B baseline91.41s24.90.17 倍(品質重視、現本番)
31B + MTP+patch26.00s86.70.58 倍31B 品質 + MTP の現実的選択肢
Python 生成速度の絶対値比較 (chars/s)
26B baseline
148.9 ch/s
26B + MTP+patch
279.8 ch/s ← 最速
31B baseline
24.9 ch/s
31B + MTP+patch
86.7 ch/s

「最速」は 26B + MTP+patch、「高速化倍率 (= MTP の効き具合) が最大」は 31B + MTP+patch です。用途によって選択が変わります (セクション 6 で扱います)。

2.4 なぜ 31B の方が 高速化倍率 が大きいのか

26B 1.83 倍 → 31B 3.52 倍の差はおよそ 2 倍です。要因は 3 つあります。

1. memory bandwidth bound がさらに支配的

baseline の絶対速度を比較すると、target が大きくなるほどメモリ読み出しの速度がボトルネックになりやすい (= memory bandwidth bound) ことがわかります。

  • 26B baseline: 15.17 秒で 600 token = 39.5 tok/s
  • 31B baseline: 91.41 秒で 600 token = 6.56 tok/s (= 26B の 1/6)

31B Dense は 26B-A4B (MoE で active 4B) より 5 〜 7 倍重い weight 読み出しが必要です。GB10 273 GB/s 帯域でメモリ読み出しの限界に達しやすく、メモリ帯域の上限で速度が頭打ちになりやすいため、MTP の amortization (K 個の token に分散・共有して 1 回の読み出しを再利用) がより大きく効きます。

2. drafter の accept rate が高い

26B mean 1.98 → 31B mean 2.18 で、effective K (実効的に進む token 数) が +10% 上がっています。

3. drafter overhead が target に対して相対的に小さい

drafter は両方とも gemma-4-*-it-assistant (832MB) で同じです。target が大きくなるほど、drafter forward (drafter モデルを 1 回走らせる処理) の相対コストが下がります。

理論最大の高速化倍率 = (mean_accepted + 1) / (1 + drafter_overhead):

targetmean_accepted + 11 + drafter_overhead理論の高速化倍率実測 (Python)実測/理論
26B~2.98~1.71.75 倍1.83 倍105%
31B~3.18~0.93.5 倍3.52 倍100%

理論との乖離が縮まる方向に効いていることがわかります。

2.5 Python execution = 「速い + 正しい」

高速化倍率の数字だけでは「MTP が正しいコードを出している」という保証にはなりません。生成された Python コードを subprocess で実際に実行し、yfinance + pandas + matplotlib のスタックで動作するかを確認しました。

取得 trace (max_tokens=1500、26B、完成版)

varianttotalchars関数名
baseline (run2)26.81s2816fetch_nikkei_monthly_returns()
MTP+patch (run3)15.58s2931get_nikkei_monthly_returns()

両モデルとも以下を達成しました。

  • yfinance API で ^N225 の 10 年分 monthly close を取得
  • pct_change() で月次リターン算出
  • [date, close, monthly_return] の DataFrame を返却
  • 119 ヶ月分のデータ取得成功
  • matplotlib で月次リターン棒グラフ (赤=下落 / 緑=上昇) を保存

End-to-end (生成 + 実行):

baseline:  26.81 + 2.35 = 29.16s
MTP+patch: 15.58 + 2.28 = 17.86s
→ end-to-end での高速化倍率 = 1.63 倍 (節約 11.30s)

「速い (1.72 倍)」と「正しい (両者とも動作するコード)」を同時に成立させました。MTP は速度だけでなく出力品質も無損失です。冒頭の動画はこの「生成競争 → 実行 → 両モデルが等価な日経 225 月次リターンチャートを描く」までを 1 本にまとめたものです。

2.6 多言語動画 (生成競争のみ)

日本語金融分析 (日銀 YCC → 銀行株)

baseline 16.47s, 60.2 ch/s → MTP+patch 13.74s, 73.6 ch/s = 1.20 倍の高速化。日本語は drafter が深い位置で予測を当てにくく高速化倍率は控えめですが、確実に速くなります。

英語マクロ分析 (Fed QT → US 10Y yield)

baseline 15.15s, 170.1 ch/s → MTP+patch 11.14s, 232.7 ch/s = 1.36 倍の高速化。英語の方が drafter の予測が accept されやすく、日本語より高速化倍率が伸びます。


3. K-sweep (num_speculative_tokens)

K=1、2、4、8 の 4 段階でコンテナを再起動し、各 K について 3 prompts × 3 runs (max_tokens=600) のトレースを取得しました。

3.1 Speedup vs K (3-run 平均、26B-A4B)

baseline (Dynamic、MTP なし): ja 16.47s, en 15.15s, py 15.17s (= 1.0 倍 基準)

Kja_financeen_financepy_code
111.89s, 1.39 倍11.57s, 1.31 倍10.74s, 1.41 倍
212.19s, 1.35 倍11.33s, 1.34 倍9.88s, 1.54 倍
411.90s, 1.38 倍11.38s, 1.33 倍8.66s, 1.75 倍
815.77s, 1.04 倍14.12s, 1.07 倍9.54s, 1.59 倍

3.2 結論

  • コード生成 / 構造化出力 (yaml/json/SQL): K=4 が最適
  • 自然言語 (日本語要約 / 英語ブログ): K=1 で十分 (K=2 〜 4 でもほぼ変わらず)
  • K=8 はどの workload でも逆効果 — pos 7 (8 番目の位置) の acceptance が 8.9% とほぼノイズレベルで、drafter forward を 8 回回すコストと reject (棄却) 後の KV cache rollback (KV キャッシュの巻き戻し) コストが accept gain (採択ぶんの利得) を食いつぶす

vLLM のデフォルト推奨は K=4 ですが、自然言語ワークロードでは K=1 でメモリと compute (計算量) を節約できます。詳細な per-position acceptance / 理論の高速化倍率との比較データは付録 A にまとめています。


4. 再現手順

4.1 26B-A4B + MTP+patch の起動コマンド

docker run -d --name vllm-mtp \
  --gpus all --ipc host --shm-size 64gb \
  -p 8000:8000 \
  -v $HOME/ai_news2fund/hf_cache:/root/.cache/huggingface \
  -v $HOME/mtp_assets/gemma4_mtp.py:/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/gemma4_mtp.py:ro \
  vllm/vllm-openai:gemma4-0505-arm64-cu130 \
  RedHatAI/gemma-4-26B-A4B-it-FP8-Dynamic \
  --served-model-name gemma4-26b-mtp \
  --max-model-len 4096 \
  --max-num-batched-tokens 4096 \
  --gpu-memory-utilization 0.85 \
  --kv-cache-dtype fp8 \
  --speculative-config '{"method":"mtp","model":"google/gemma-4-26B-A4B-it-assistant","num_speculative_tokens":4}'

4.2 31B Dense + MTP+patch の起動コマンド

docker run -d --name vllm-mtp \
  --gpus all --ipc host --shm-size 64gb \
  -p 8000:8000 \
  -v $HOME/ai_news2fund/hf_cache:/root/.cache/huggingface \
  -v $HOME/mtp_assets/gemma4_mtp.py:/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/gemma4_mtp.py:ro \
  vllm/vllm-openai:gemma4-0505-arm64-cu130 \
  RedHatAI/gemma-4-31B-it-FP8-Dynamic \
  --served-model-name gemma4-31b-mtp \
  --max-model-len 4096 \
  --max-num-batched-tokens 4096 \
  --gpu-memory-utilization 0.85 \
  --kv-cache-dtype fp8 \
  --speculative-config '{"method":"mtp","model":"google/gemma-4-31B-it-assistant","num_speculative_tokens":4}'

--enforce-eager--enable-chunked-prefill も不要です。patch (bind mount) だけで動きます

4.3 patch ファイルの取得

PR #41745 の head commit d8b3826 から gemma4_mtp.py を直接ダウンロードして $HOME/mtp_assets/gemma4_mtp.py に保存してください。

mkdir -p $HOME/mtp_assets
curl -fsSL -o $HOME/mtp_assets/gemma4_mtp.py \
  https://raw.githubusercontent.com/vllm-project/vllm/d8b3826648da6b407f8c55457a2103be9aeb5d83/vllm/model_executor/models/gemma4_mtp.py

4.4 メモリ要件

  • 26B-A4B: target 26GB FP8 + drafter 832MB BF16 + KV cache (max-model-len 4096 で約 4GB) ≈ 約 31GB GPU RAM
  • 31B Dense: target 31GB FP8 + drafter 832MB BF16 + KV cache ≈ 約 36GB GPU RAM

GB10 121.6GB unified では十分余裕があります。DGX Spark / B200 / H100 80GB / RTX 6000 48GB でも問題なく動作する見込みです。

4.5 検証で使ったプロンプト全文

再現性のため、本検証で使った 3 種類のプロンプトを全文掲載します (定義: tools/mtp_traces/prompts.json)。temperature=0.7、max_tokens=600 (Python のみ後段で 1500 も併用)。

ja_finance — 日本語金融分析タスク

日銀のYCC撤廃が日本の銀行株 (三菱UFJ / 三井住友 / みずほ) に与える影響を、NIM・含み損・配当方針の3点で詳細に解説してください。各論点について、足元の数値や具体的な政策のメカニズムを盛り込み、投資家が実務で使える形で整理してください。

en_finance — 英語マクロ経済分析タスク

Explain how the Fed’s quantitative tightening (QT) affects the US 10Y treasury yield through three channels: TGA drawdown, RRP utilization, and bank reserve scarcity. Include current data context (2025-2026), the transmission mechanism for each channel, and provide three actionable insights for fixed income traders managing duration risk.

py_code — Python コード生成タスク (PEP 8 + type hints + error handling 指定)

Write a Python function using yfinance that fetches monthly close prices for the Nikkei 225 (^N225) for the past 10 years, computes month-over-month returns, and returns a pandas DataFrame with columns [date, close, monthly_return]. Include error handling for missing data and an example usage block. Use type hints and follow PEP 8.

選定基準: ① 筆者の本番ワークロードを反映する 3 タイプ (日本語要約 / 英語要約 / コード生成)、② drafter accept 難度が異なる (自然言語の語順自由度 vs コードの決定性)、③ 同じ max_tokens で 600 〜 2900 chars の出力幅をカバー (CJK は 1 token ≒ 1.5 〜 2 char、英語は 1 token ≒ 4 char、コードは中間)。


5. 背景: なぜ patch が必要なのか

公開直後の vllm/vllm-openai:gemma4-0505-arm64-cu130そのまま使うと、量子化された target ではどんな構成でも acceptance rate が 0% になり、MTP は事実上動作しません--enforce-eager を入れても --kv-cache-dtype fp8 を外しても num_speculative_tokens を 1 に下げても変わりませんでした。

原因は 2 箇所、PR #41745 のコメントで @hospedales が特定し、patch を投稿しています。

#バグ箇所内容影響
1intermediate_size 取得位置 (line 297)drafter MLP は text_config.intermediate_size = 8192 が必要だが、元コードは top-level config から 4096 を pull → MLP が半サイズweight load が失敗するか、計算結果が壊れる
2quant_config propagation (lines 186/193/299)target の quant_config (FP8-block / FP8-Dynamic / NVFP4) を drafter Linear layer に伝播してしまう。drafter は本来 BF16 weights を持つvLLM が drafter param に packing を適用して shape 不一致 → weight load 段階で破損 or silent な不具合。量子化された target で必ず発症

仕組みとしては、Docker image (gemma4-0505-arm64-cu130) は PR の最初の commit でビルドされており、後から landed (取り込まれた) bug fix を含みません。PR の head commit d8b3826gemma4_mtp.py を bind mount で当てるのが、現状の最短解です。

教訓:

  1. 公開直後の vLLM image は信用しない — PR の最初の commit が image にバンドルされると、後から landed した bug fix が反映されない
  2. 第三者の動作確認済み事例 (working config) では「完全なコマンド」を読み込む — bind mount の patch がブログ本文の外側に書かれていた事例があり (ai-muninn の DGX Spark 26B 解説)、本文中の docker run コマンドだけ見ていては気付けない
  3. 修正は upstream で merge され次第 image に反映されるはずだが、それまでは bind mount で運用するのが現実的

6. 本番採用の指針

用途推奨構成期待性能
速度最優先 (コード生成、構造化出力)26B-A4B + MTP+patch、K=4Python 8.30 秒で完成、英語 270+ chars/s
31B 品質が必要、ただし速度も重要31B Dense + MTP+patch、K=431B baseline の 28% で完了 (= 3.52 倍速)、十分実用的な体感速度
自然言語要約 (日本語ニュース要約など)26B / 31B + MTP+patch、K=1K=4 と高速化倍率はほぼ同じで、メモリと compute を節約できる
品質と速度のバランス26B-A4B baseline (MTP なし)39.5 tok/s で多くのケースに十分、構成も単純

筆者の本番 (現状 31B-FP8-block 6.46 tok/s) は 31B-Dynamic + MTP+patch (約 28.5 tok/s、4.4 倍 faster) で同品質となるため、本番採用を真剣に検討する価値があります (要 K=1 検証で日本語適合確認)。


付録 A: K-sweep の詳細データ

A.1 acceptance metrics (累積 9 trace × 4 K、26B-A4B)

Kdraftsdraft tokacceptedoverall ratemean acceptedper-position rate (%)
130273027236778.2%0.78/178.2
223064612309167.0%1.34/276.2 / 57.8
418147256359149.5%1.98/472.2 / 53.6 / 41.2 / 30.9
8157612608382930.4%2.43/873.4 / 52.3 / 37.1 / 25.8 / 19.3 / 15.2 / 11.0 / 8.9

A.2 mean accepted length と高速化倍率の理論比較 (26B、Python)

理論上、高速化倍率 ≒ (mean_accepted + 1) / (1 + drafter_overhead) ≒ mean_accepted + 1 です。

Kmean accept理論の高速化倍率実測 (py)実測/理論
10.78約 1.78 倍1.41 倍79%
21.34約 2.34 倍1.54 倍66%
41.98約 2.98 倍1.75 倍59%
82.43約 3.43 倍1.59 倍46%

K が大きいほど理論値との乖離が拡大します。drafter forward (drafter の追加実行)、KV cache rollback (棄却時のキャッシュ巻き戻し)、scheduling overhead (スケジューリング負荷) が顕在化するためで、これが「K=8 が逆効果になる」正体です。

A.3 31B の acceptance metrics (K=4、累積 9 trace + 1 probe)

drafts          = 1890
draft_tokens    = 7560 (= 1890 × 4)
accepted        = 4117
overall accept  = 54.5%   (26B は 49.5%)
mean accepted   = 2.18 / 4   (26B は 1.98 / 4)

per-position acceptance:
  pos 0: 78.7%   (26B: 72.2%)
  pos 1: 58.9%   (26B: 53.6%)
  pos 2: 45.6%   (26B: 41.2%)
  pos 3: 34.6%   (26B: 30.9%)

出典

この記事をシェア

この記事についてのLinkedIn投稿でコメントや意見を共有できます。

LinkedInで議論する

関連分析