技術 約7分で読めます

Gemma 4 MTP drafterをM1 Max 64GBで実測、26B A4Bだけ速くなって31BとE4Bは遅くなった

いけさん目次

Gemma 4 MTP drafterをM1 Max 64GB + mlx-vlm 0.5.0で実機検証した。
結果は公式の予測どおりには動かず、26B A4B (MoE) だけ+13%速くなり、31B Denseと E4Bは逆に遅くなった。
このメモは公式ブログとvLLM recipeを読んで書いた前回の記事の続編で、実測値とプロンプト依存性に絞る。

検証環境

  • Apple M1 Max 64GB ユニファイドメモリ
  • macOS Sonoma、Python 3.13.11、mlx-vlm 0.5.0、mlx-lm 0.31.3
  • 推論バックエンドはmlx-vlm(CLIで --draft-kind mtp --draft-block-size N をサポートする)
  • 配布モデルは mlx-community org のものをそのまま使用
    • 本体: gemma-4-E4B-it-bf16 / gemma-4-26B-A4B-it-4bit / gemma-4-31B-it-4bit
    • drafter: gemma-4-{E4B,26B-A4B,31B}-it-assistant-bf16(drafterは全部bf16のみ配布)

vLLM recipeでは26B A4Bと31Bにtensor parallel 2が推奨されているが、Apple Siliconはbatch size 1の単発リクエストで動かすので、ここはそのまま単発で叩く。
公式が「Apple Siliconのbatch 1ではrouting的に難しい」と書いた条件を、そのまま実機で踏みに行く構成だ。

プロンプトと条件

メイン検証は、出力が長く構造的になるコード生成プロンプト1本:

Write a Python function called fibonacci_memoized that computes
the n-th Fibonacci number using memoization. Include type hints,
a docstring, and an example usage block at the bottom.

--max-tokens 256、drafter付きは全て --draft-block-size 4(vLLM recipe推奨ベース)。

事前のスモークテストでは、出力が短く創作寄りになる短文haikuプロンプトも別途試した:

Write a haiku about the ocean.

こちらは --max-tokens 50 で、生成は20トークン前後で終わる。
このプロンプトは全モデルでMTPが遅くなる結果になった。
出力長と内容で結論が変わるので、本記事ではコード生成側を主、haiku側を比較として両方の数字を並べる。

E4B bf16はdrafter overheadが勝った

E4Bは本体が4Bと小さく、drafter (78M) を付けても受理できれば効くはずだったが、実測は逆だった。

条件tokens-per-sec受理率
baseline29.6
MTP block_size=418.10.74 / 147 rounds
MTP block_size=6 (haiku)12.90.62 / 13 rounds

受理率0.74/round、つまり1ラウンド4トークン提案して0.7程度しか受理されない。
本体モデルが軽いほど1トークンあたりの生成コストが低く、drafterの呼び出しコストとverify計算が相対的に重くなる。
論文や公式ブログが「drafterは本体よりはるかに小さく作る」と書いている前提が、ここでは本体側がもう十分軽いせいで効きにくい。

短文haikuの方は受理率がさらに低い。
これは創作系プロンプトで次トークンの予測難度が高いせいで、drafterが当てにいけないことが理由として大きいと思う。

26B A4B 4bitはMoEなのに唯一speedupした

公式ブログとAI for Developersのドキュメントは、MoEモデルではbatch 1で各トークンが別のexpertを叩くのでverifyフェーズで追加expert weightのロードが入り、drafterの得が相殺されると書いていた。
ところが実測は逆で、3モデルで唯一speedupが出た。

条件tokens-per-sec受理率
baseline57.4
MTP block_size=465.12.41 / 75 rounds

+13%、受理率は1ラウンド4トークン中2.41で約60%。
コード生成は構造が読みやすく、defreturnif x is None: のような定型を含むので、drafterが当てやすい。
MoEのexpert routing問題が、コード生成というタスク特性で吸収されている可能性が高い。

baseline側で26B A4Bが57.4tok/sも出ているのもApple Silicon × MoE × 4bit量子化の組み合わせ的に強い。
active 4Bしか動かないMoE構造が、ユニファイドメモリ帯域に対しては向いている、というのが裏でのからくりだろう。

31B Dense 4bitは公式が「一番効く」と言ったのに遅くなった

drafterで最も恩恵を受けるはずの重い本体モデル、31B Dense。
本体30.7Bを毎トークン読むので、4-8トークン先読みできれば理論上は大きく速くなる、というのが公式の説明だった。
実測:

条件tokens-per-sec受理率
baseline15.2
MTP block_size=414.32.37 / 76 rounds

−6%、受理率は26B A4Bとほぼ同じ60%。
受理率は出ているのに遅くなる、というのが面白いところだ。

数字を分解すると、baselineは256トークンを16.8秒、MTPは17.9秒。
ラウンドあたりのdrafter overheadは(17.9 - 16.8) / 76 ≈ 14msで、26B A4Bでの約7msより明確に大きい。
target modelが重いとverify計算もそのぶん重くなる。
受理率60%でも、verify込みのコストを償却しきれずに微減で着地した。

vLLM recipeで31Bに num_speculative_tokens 4-8 とtensor parallel 2が推奨されているのは、まさにこのverifyコストをGPU並列で食わせて隠す狙いだろう。
Apple Silicon単発の構成では、その並列化の余地が小さい。

公式予測と実測のずれを整理

3モデル分の結果を1表にまとめた:

モデルbaseline tok/s+ MTP tok/s差分受理率公式予測
E4B bf1629.618.1−39%0.74/rdrafterで速くなる前提
26B A4B 4bit57.465.1+13%2.41/rbatch 1で伸びにくい
31B 4bit15.214.3−6%2.37/r一番効くはず

公式の3つの予測すべてと逆向きの結果が出た。
ただし「公式が間違っている」という話ではなく、検証環境の前提(vLLM recipe = データセンタGPU + tensor parallel + batch複数)と、こちらの環境(Apple Silicon + 単発リクエスト + 4bit量子化)が別物すぎて、優先順位がそのまま転倒したという読み方が近い。

flowchart LR
  A[公式想定] --> B[H100/A100 + TP2 + batch複数]
  B --> C[31B Denseで最大speedup]
  D[実測環境] --> E[M1 Max + batch 1 + 4bit量子化]
  E --> F[26B A4Bがspeedup<br/>31BとE4Bは遅くなる]

26B A4Bだけ速くなる条件

ユニファイドメモリ帯域が4bit量子化のMoEに合っているのが大きい。
26B A4Bはアクティブ4Bだけが各トークンで動く設計で、4bit量子化と組み合わせると毎トークンの読み込み量が小さい。
MTPで先読みしたぶんverify時にexpert weightをまとめて読めるので、メモリI/Oがある程度まとまる。

これに加えて、drafter (0.4B bf16) と本体 (active 4B in 4bit) のサイズ比が悪くない。
drafterのbf16実体が本体active側に対して相対的に重すぎないので、提案コストが許容範囲に収まる。

最後にコード生成というタスク特性。
defreturn)、改行+インデントといった定型トークンが多く、drafterが当てやすい。
受理率60%はこの効果が大きい。

E4Bがダメな理由は、本体が軽すぎてdrafterのoverheadを償却できないこと。
31Bがダメな理由は、target側のverify計算が4bit量子化でも重く、Apple Siliconの単発実行ではTP2のような分散ができないこと。
両端が落ちて、中央の26B A4Bだけ条件が合った形だ。

短文haikuだとMoEでも遅くなる

スモーク段階で20-50トークンの短文haikuを試したときは、3モデル全部で遅くなった:

モデルbaseline+ MTPblock受理率
E4B bf1630.412.960.62 / 13
26B A4B 4bit55.936.241.27 / 11
31B 4bit15.69.241.33 / 9

短文ではdrafterのoverheadを償却するだけのトークン数が出ないし、創作プロンプトで受理率も下がる。
コード生成256トークンと短文haiku20トークンで結論が逆転する。
これは「MTPは速くなる」「MTPは遅くなる」のどちらも真で、プロンプト長と内容に強く依存することを示している。

検索流入で「Gemma 4 MTPは速いのか遅いのか」を知りたい読者向けに言えば、答えはプロンプト次第で、特に短文チャット用途ではむしろ遅くなる側に振れやすい。

ローカルで試す前に確認すべきこと

実機で同じ検証をしたい人向けの注意点:

  • mlx-vlm 0.5.0以降が必要(--draft-kind mtp フラグの有無で確認)
  • 6モデル合計で約45GB(drafter 3つ + 本体 3つ、量子化込み)。ディスク残量は要注意
  • mlx-vlmは生成末尾に Speculative decoding: X accepted tokens over Y rounds を表示する。受理率を自前計測する必要はない
  • block_sizeはモデルごとに最適値が違う。E4Bは2か3、26B A4Bは4、31Bは4-6あたりから始めるのが無難
  • 26B A4Bは4bit量子化でも18GB弱、drafter込みで20GB前後使う。64GBあれば余裕だが32GBだと厳しい

26B A4Bの実測値だけは試す価値がある。
公式ドキュメントを信じるなら「batch 1で伸びない」と書いてあるので試さない選択になるが、コード生成用途では実機で唯一のspeedup源になっている。

参考