昨夜、dense 30Bモデル、256Kコンテキスト、Vision、speculative decodingを、1枚の24GB GPUに収める実験を行った。
最速の量子化は負けた。perplexityが最も低い量子化も負けた。
勝ったのは、どれか1つの数字が最大になる構成ではなく、システム全体として最も使える構成だった。
最終構成は、70Wに制限したNVIDIA RTX PRO 4000 Blackwell SFF上で、Meta Muse Glimmer 30B、別体のVision projector、5層のDFlash drafter、262,144 token slotを同時に動かす。
結論を先に書く。
speculative decodingを使うと、tokens per secondは純粋なハードウェア指標ではなくなる。出力がどれだけ予測可能かを測る指標にもなる。
同じモデル、同じGPU、まったく違う速度
ターゲットは同じQ5_K_M、GPUも同じである。
| Workload | Decode | Acceptance | 意味 |
|---|---|---|---|
| 通常decode、code | 17.98 tok/s | n/a | target単体 |
| DFlash、code | 80.87 tok/s | 36.18% | 構造化された予測しやすい出力 |
| 最良の256K tuning | 84.64 tok/s | 38.67% | Visionも同時ロード |
| code + prose + planning + infra | 38.34 tok/s | 14.41% | 実際のagent workload |
| 262,116 input tokens後 | 21.56 tok/s | workload依存 | KV cacheをほぼ完全に使用 |
codeはspeculative decodingと相性が良い。構文、indentation、API、識別子、JSON schema、boilerplateが次のtoken候補を狭くするからだ。
一方、architecture planningや会話文では、複数の続きが同時に正しい。drafterとtargetが最初の1 tokenで別の正解を選ぶと、その後のspeculative prefixはすべて失効する。
つまりagentには少なくとも2つの速度領域がある。実装やstructured outputは速い。open-ended reasoningは同じようには伸びない。
Acceptance rateだけを見ると誤る
draft lengthの比較は特に分かりやすかった。
| Maximum draft | Acceptance | Decode |
|---|---|---|
| 4 tokens | 47.46% | 34.50 tok/s |
| 8 tokens | 27.00% | 40.54 tok/s |
| 12 tokens | 20.33% | 42.22 tok/s |
| 15 tokens | 17.99% | 47.20 tok/s |
Draft 4はacceptanceで勝ち、throughputで負けた。Draft 15はacceptanceが大幅に低いのに36.8%速かった。
より重要な補助指標はaverage acceptance length、つまりτである。1回の高価なtarget passで、平均何tokenを確定できたかを表す。今回のcoding runではτ=6.39まで伸びた。
CPUがGPUに静かなブレーキをかけていた
最大の実装上の改善は、新しい量子化ではなかった。
drafter自体はGPUで動いていたが、greedyなdraft token selectionのargmaxがCPU pathを通っていた。そのため、最も熱いloopにdevice-to-host転送と同期が入る。
argmaxをGPU backend graph内に残すと、mixed benchmarkは36.30から38.34 tok/sへ5.6%改善した。予測しやすいcodeでは、この小さな境界削除がDFlash全体の効果と重なり、target単体17.98 tok/sに対して80.87 tok/sまで伸びた。
speculative decoderはcheckboxではなくpipelineである。小さなCPU処理1つが、高速なGPU graph全体のmetronomeになり得る。
最速のNVFP4を採用しなかった理由
実験的なNVFP4 hybridは94.36 tok/sに達した。しかしWikiText-2 perplexityは5.6493で、Q5_K_Mの5.5420より悪かった。
Q5_K_XLはperplexityを約0.26%だけ改善した一方、throughputは約13%低下し、DFlash acceptanceも悪化した。そこで最終的にQ5_K_Mを選んだ。
量子化のlabelだけでは判断できない。embeddingやlm_headを高精度で残すか、layerごとにprecisionを変えるか、imatrixを使うか、architecture固有のtensor変換とscaleが正しくgraphに入っているかで結果は変わる。
perplexityとspeculative compatibilityは別の軸である。
sampling defaultだけで8.1%変わった
Muse Glimmerの推奨値はtemperature 1.0、top-p 0.95、top-k 64である。これらを設定しても、llama.cppのdefault min-p=0.05が残っていた。
min-p=0にしてdocumented samplerへ合わせると、最終runは78.29から84.64 tok/sへ8.1%改善し、acceptanceも36.18%から38.67%へ上がった。
samplingは品質設定であると同時に、speculative performance設定でもある。
256Kは「loadできた」だけでは検証にならない
--ctx-size 262144でserverが起動するだけでは不十分だ。KV memoryはlazy allocationされる場合があり、prefill中にCUDA work bufferが増え、長いcache positionではattention costも変わる。
今回は実際に262,116 input tokensを入れ、その後にdecodeした。
- OOMなし
- prompt processing: 371.24 tok/s
- far-cache decode: 21.56 tok/s
- VRAM: 22,920 / 24,467 MiB、93.68%
- free VRAM: 1,547 MiB
- quantized Vision projectorを同時ロード
- 実画像requestも成功
空の256K slotと、実際に埋まった256K KV cacheは別のworkloadである。context capacityとcontext performanceは同じ主張ではない。
最終構成
- Target: Muse Glimmer 30B UD-Q5_K_M
- Context: 262,144 tokens
- Vision: quantized Muse
mmproj - Speculation:
dflash-kquant,n_max=15 - Target KV: Q8
- Drafter KV: F16
- Execution: GPU argmax、CUDA graph optimization、batch/ubatch 1024/256、threads 8/8
- Sampling: temperature 1.0、top-p 0.95、top-k 64、min-p 0
- Short-code result: 84.64 tok/s、38.67% acceptance
benchmarkで最低限報告すべきもの
- target modelと正確なquant recipe
- drafterとdraft length
- code、planning、chat、tool callのworkload比率
- acceptance rateとaverage acceptance length τ
- すべてのsampling parameters
- prefillとdecodeを分離した数字
- KV cache position
- 実際にfillした後のVRAM
- draft token selectionがGPU内に残るか
- speedと並べたquality指標
単一のtok/sだけでは、speculative decodingのbenchmarkとして情報不足である。何を生成したか、何tokenが1回のtarget passで確定したか、CPU/GPU境界がどこにあるか、cursorがKV cacheのどこにいたかまで書く必要がある。
完全な表、実装上の不具合、量子化比較、参照リンクは英語版にまとめた。
Original: https://piszczek.pl/blog/dflash-changes-what-tokens-per-second-means