Gemma 4 31B ITを RTX PRO 6000 Blackwell 96GB 1枚で動かし、vLLM 0.21.0 と SGLang gemma4-mtp を比べました。coding-agent の作業で、KV cache dtype、KV pool、MTP投機デコードの違いを確認します。

vLLM は NVFP4 / FP8 block、SGLang は FP8 block + BF16 KV / FP8 KVを使いました。速度とKV容量はvLLM、ツール利用と出力の印象はSGLang FP8 KVが良かったです。生成内容は動画に残しています。

動画

SGLang の実行動画です。

動画リンク: https://www.youtube.com/watch?v=YlysBl5Qg34

動画リンク: https://www.youtube.com/watch?v=cgYbqnwqygM

比較一覧

RedHatAI の同じ FP8 モデルを ctx 256K で確認しました。NVFP4 は NVIDIA のモデルです。

ConfigRuntimeWeightsKV cacheKV poolPeak TGTypical TGPeak acceptNotes
AvLLM 0.21.0NVFP4FP8876K tok109.8 tok/s90-100 tok/s99.5%262K ctx、MTP x4
BvLLM 0.21.0FP8 blockFP8876K tok115.3 tok/s80-100 tok/s93.5%262K ctx、MTP x4
CSGLang gemma4-mtpFP8 blockBF1669.7K tok約80 tok/s55-75 tok/s約90%262K ctx、SWAKVPool、MTP x5/6
DSGLang gemma4-mtpFP8 blockFP8139.5K tok約93.8 tok/s70-90 tok/s約96%262K ctx、SWAKVPool、MTP x5/6
  • NVFP4では繰り返しに陥ることがありました。samplingか設定のミスかもしれません。
  • 単一ストリームのTGは vLLM + MTP + prefix caching が最も高い帯でした。
  • vLLMの速度はMTP accept rateに連動して2つの帯に分かれました。
  • SGLangのFP8 KVは、同じSWAKVPool容量でBF16 KVの約2倍のtokenを確保できました。
  • SGLang FP8 KVは速度が低いものの、coding-agentの出力は良いと感じました。min_pも設定できます。

比較したかったこと

比較した条件は5つです。

  • Runtime: vLLMとSGLang
  • Weight quantization: NVFP4とFP8 block
  • KV cache dtype: FP8とBF16
  • Context budgetとKV pool topology: vLLMのflat KVとSGLangのsliding-window KV pool
  • Speculative decoding: MTP x4とMTP x5/6

coding-agent は長い文脈、tool call、自由記述、構造化出力を扱います。LLM導入時の速度を見積もるため、MTPの受理率が高い区間と低い区間を確認しました。

Hardware

項目値
GPUNVIDIA RTX PRO 6000 Blackwell 96GB (SM120)
HostAMD EPYC、768GB DDR5 ECC

A. vLLM 0.21.0: NVFP4 weights + FP8 KV

AはNVFP4 4-bit weights、FP8 KV、Gemma 4 Assistant draft modelによるMTPです。contextは262,144、num_speculative_tokens=4 にしました。(公式cookbook)

Configuration

項目値
RuntimevLLM 0.21.0
GPURTX PRO 6000 Blackwell 96GB
QuantizationModelOpt NVFP4 weights
KV cacheFP8
Context length262,144
Tensor parallel1
Speculative decodingGemma 4 MTP、4 speculative tokens
Attention backendTriton Attention
Sampling backendFlashInfer
Compile backendtorch.compile / Inductor

起動コマンド

実行コマンドです。再利用時に残った不要なenvも含みます。

  podman run --rm \
  --name test-model \
  --device nvidia.com/gpu=0 \
  --ipc=host \
  -p 8000:8000 \
  -v /mnt/data/models:/hf/hub:ro \
  -e HF_HOME=/hf \
  -e HF_HUB_OFFLINE=1 \
  -e TORCH_CUDA_ARCH_LIST=12.0 \
  -e VLLM_FLASHINFER_MOE_BACKEND=throughput \
  -e VLLM_TARGET_DEVICE=cuda \
  -e VLLM_USE_FLASHINFER_MOE_FP4=1 \
  registry.home.arpa/vllm/vllm-openai:v0.21.0-ubuntu2404 \
  /hf/hub/models--nvidia--Gemma-4-31B-IT-NVFP4/snapshots/e5ef03afa233c35cb000323ff098d4291e1dd07c \
  --host 0.0.0.0 \
  --port 8000 \
  --served-model-name test-model \
  --dtype auto \
  --kv-cache-dtype fp8 \
  --tensor-parallel-size 1 \
  --trust-remote-code \
  --gpu-memory-utilization 0.85 \
  --max-num-seqs 1 \
  --async-scheduling \
  --enable-auto-tool-choice \
  --tool-call-parser gemma4 \
  --reasoning-parser gemma4 \
  --speculative-config '{"model":"/hf/hub/models--google--gemma-4-31B-it-assistant/snapshots/4735700dca7bd22fad5dc348c228b50ec6cbac6d","num_speculative_tokens":4}' \
  --max-model-len 262144 \
  --override-generation-config '{"temperature":0.9,"top_p":0.85,"top_k":64,"presence_penalty":0.0,"repetition_penalty":1.13}'
  

実測ログ

実行中の代表値です。

  Generation throughput: 95.9 tok/s   acceptance length: 4.28   draft acceptance: 82.0%
Generation throughput: 95.3 tok/s   acceptance length: 4.18   draft acceptance: 79.5%
Generation throughput: 109.8 tok/s  acceptance length: 4.97   draft acceptance: 99.2%
Generation throughput: 107.6 tok/s  acceptance length: 4.98   draft acceptance: 99.5%
  

集計値です。

指標値
Generation throughput最大109.8 tok/s
Typical active generation90-100 tok/s前後
Mean MTP acceptance length最大4.98、max 5相当
Average draft acceptance rate80-90%前後
Best draft acceptance rate99.5%
GPU memory used for model load32.06 GiB
KV cache size876,006 tokens

速度は95 tok/s前後と107 tok/s以上に分かれました。GPU loadよりMTP accept rateに連動しています。

KV cacheは876K tokensで、262K contextの約3.3倍です。ただし --max-num-seqs 1 なので、単一セッションでは余剰分を使い切りません。tool-callの安定性も調べました。vLLM + MTP、text-onlyでは min_p が使えず、temperature、top_p、top_k、repetition_penalty を調整しましたが、安定した設定は決まりませんでした。

崩れた場面では、Zedが (Sigh) と (Laughter) を繰り返しました。その間もvLLMはaccepted/drafted throughputとacceptance rateを出し続けていました。

NVFP4構成でZed Agentの出力がSighやLaughterの反復に崩れている画面
NVFP4構成で繰り返し崩れが出た場面。左のZed Agent出力が(Sigh)や(Laughter)へ反復し、右には同時刻のvLLM MTPログが出ている。

B. vLLM 0.21.0: FP8 block weights + FP8 KV

BはAと同じvLLM 0.21.0で、weightsだけNVFP4からFP8 blockへ変えました。KVはFP8のままです。

Configuration

項目値
RuntimevLLM 0.21.0
GPURTX PRO 6000 Blackwell 96GB
QuantizationFP8 block
KV cacheFP8
Context length262,144
GPU KV cache size876,006 tokens
Speculative decodingGemma 4 Assistant draft model、num_speculative_tokens=4

実測値

  Generation throughput: up to 115.3 tok/s
Typical active generation: around 80-100 tok/s
Mean MTP acceptance length: up to 4.74
Average draft acceptance rate: around 80-88%
Best draft acceptance rate: 93.5%
GPU KV cache size: 876,006 tokens
  

AからBで、peak throughputは109.8から115.3 tok/sへ上がり、peak draft acceptanceは99.5%から93.5%へ下がりました。FP8 KVは共通で、KV poolも876K tokensのままです。

C. SGLang gemma4-mtp: FP8 block weights + BF16 KV

CはSGLang gemma4-mtp、FP8 block weights、BF16 KVです。sliding-window対応の SWAKVPool なので、vLLMのflat KVとは容量の見え方が異なります。

前日に公開された lmsysorg/sglang:gemma4-mtp のimageを使いました。NEXTNからFROZEN_KV_MTPへ変わったコミットかもしれません。

Docker Hubでlmsysorg/sglangのgemma4-mtpタグを確認している画面
SGLang gemma4-mtpのDocker imageタグ。gemma4-mtpはlinux/amd64、gemma4-mtp-cu13とgemma4-mtp-arm64-cu13はlinux/arm64として表示されている。ややこしいけど、Linuxでなおかつarm64なので注意。Linux or arm64と勘違いして無駄なDLした。

Configuration

項目値
RuntimeSGLang gemma4-mtp
GPURTX PRO 6000 Blackwell 96GB
QuantizationFP8 block
KV cache dtypetorch.bfloat16
Context length262,144
Speculative decodingNEXTNからFROZEN_KV_MTPにauto-promote
Draft modelGemma 4 Assistant draft model
Speculative stepsspeculative_num_steps=5
Draft tokensspeculative_num_draft_tokens=6

SWAKVPool初期化値

  full_layer_tokens=69757
swa_layer_tokens=55805
max_total_num_tokens=69757
  

KV memory

  SWA KV:  21.29 GB K + 21.29 GB V
Full KV:  2.66 GB K +  2.66 GB V
Total SWAKVPool usage: 47.90 GB
  

実測値

  Typical decode throughput: around 55-75 tok/s
Peak throughput: around 80 tok/s class
Typical accept length: around 4.5-5.5
Typical accept rate: around 75-90%
  

Cの典型速度は55-75 tok/s、代表的なpeakは80 tok/s台でした。ログにはlow 90sの例もありますが、比較表では約80 tok/sとしています。KVは69.7K tokensで、vLLMの876K tokensより小さいです。

D. SGLang gemma4-mtp: FP8 block weights + FP8 KV

DはCのKV dtypeだけを torch.float8_e4m3fn に変えました。今回のSGLang構成では、coding-agentの作業が最も良いと感じました。

Configuration

項目値
RuntimeSGLang gemma4-mtp
GPURTX PRO 6000 Blackwell 96GB
QuantizationFP8 block
KV cache dtypetorch.float8_e4m3fn
Context length262,144
Speculative decodingNEXTNからFROZEN_KV_MTPにauto-promote
Draft modelGemma 4 Assistant draft model
Speculative stepsspeculative_num_steps=5
Draft tokensspeculative_num_draft_tokens=6

SWAKVPool初期化値

  full_layer_tokens=139515
swa_layer_tokens=111612
max_total_num_tokens=139515
  

KV memory

  SWA KV:  21.29 GB K + 21.29 GB V
Full KV:  2.66 GB K +  2.66 GB V
Total SWAKVPool usage: 47.90 GB
  

実測値

  Typical decode throughput: around 70-90 tok/s
Peak throughput: around 93.8 tok/s
Typical accept length: around 5.0-5.8
Typical accept rate: around 80-96%
  

Zed AgentでDjangoの予約管理機能を生成しながら、SGLangのdecode batch、accept length、accept rate、generation throughputを確認しました。

SGLang gemma4-mtpでcoding-agentがDjango予約管理機能を生成している画面
SGLang gemma4-mtpで生成中の画面。左ではZed AgentがDjangoのrestaurant reservation実装を進め、右ではSGLangのdecode batchログとaccept rate、generation throughputを確認している。

Cと同じ47.90GBのSWAKVPoolで、full-layer tokensは69,757から139,515、SWA layer tokensは55,805から111,612へ増えました。ほぼ2倍です。

FP8 KV scaling factorsが1.0にdefaultされた警告が出ました。今回の範囲では目に見える品質低下はありませんでした。

横断比較

C→Dはweightsを変えず、KVをBF16からFP8にして69.7Kから139.5K tokensへ増えました。A→Bはweightsを変えましたが、FP8 KVは共通で876K tokensでした。今回の容量差はKV dtypeに強く依存しています。

同程度のweight precisionでは、vLLMのraw throughputが高い結果でした。SGLangの出力品質と追従の良さは、今回の作業での主観的な評価です。

観点一番良かった構成理由
最高peak throughputB: vLLM FP8 block + FP8 KV115.3 tok/sまで伸びた
日常のthroughput安定性A: vLLM NVFP4 + FP8 KV90-100 tok/s帯で安定し、accept peakも高い
MTP acceptance ceilingA: vLLM NVFP4 + FP8 KV99.5%まで見えた
SGLangの実用構成D: SGLang FP8 block + FP8 KVBF16 KVよりcapacityが大きく、体感品質も良かった
KV capacityA/B: vLLM FP8 KV876K tokensを確保

採用した構成と比較条件

Dの典型decodeは70-90 tok/sでした。速度だけでなく、ツール利用と出力の安定感を重視して選びました。

比較の条件と未解決の点です。

  • vLLMは--max-num-seqs 1の単一ストリーム測定。
  • vLLMの値はruntime-reported accepted throughput、SGLangの値はdecode throughputであり、完全に同じ指標ではない。
  • MTPを使う構成ではGemma 4 Assistant draft modelを使っている。
  • NVFP4は繰り返しで破綻する場面があり、samplingか設定に未確認の問題が残っている可能性がある。

私はSGLang FP8を採用しました。役割に合わせ、CTX 96-128K、KV FP8で使う想定です。