AI・信頼性・評価
投機的デコーディングとは:同時リクエストが多いと遅くなる理由
- 投機的デコーディング
- LLM推論
- vLLM
- SGLang
- EAGLE
- MTP
- ShareGPT
- ドラフトモデル
- ターゲットモデル
- 先読み数
- 受理の長さ
- バッチ
- TPOT
- 順伝播
- メモリバウンド
- コンピュートバウンド
- 指数移動平均
- KVキャッシュ
- データ並列
- H100
- MTBench
- MagicDec
目次
オープンウェイトの LLM を、自前の LLM推論のサーバーで配信するエンジニアを考える。LLM は、文章を分けた単位であるトークンを一つずつ生成する。トークンを一つ生成するたびに、LLM はモデルの全層を入力から出力まで一回計算する。この一回の計算を順伝播と呼ぶ。利用者の待つ時間は、出力トークン一つあたりの時間で決まり、これを TPOT と呼ぶ。
投機的デコーディングは、TPOT を縮める手法である。小さく速いモデルが次の数トークンを予想し、配信したい本来のモデルが、その予想を一回の順伝播でまとめて確かめる。小さなモデルをドラフトモデル、本来のモデルをターゲットモデルと呼ぶ。予想が当たった分だけ、ターゲットモデルの一回の順伝播で複数のトークンが進む。一回に予想するトークンの数を先読み数と呼び、K と書く。
エンジニアは、利用者が一人のとき、この手法で TPOT が縮むことを確かめる。ところが、サーバーが同時に処理するリクエストの集まり(バッチ)が大きくなると、TPOT が手法を使わない場合より長くなることがある。LLM の配信エンジンである vLLM と SGLang は、どちらもこの問題に対処する機能を入れ、その設計と理由を文書とプルリクエスト(変更の提案と記録)に書いている。
投機的デコーディングは、なぜ同時に処理するリクエストが多いと遅くなり、vLLM と SGLang はそれにどう対処しているのか。
投機的デコーディングは、確かめるトークンを増やしても順伝播の時間がほとんど伸びない間は速いが、同時のリクエストが多いと確かめる計算で順伝播の時間が伸び、使わない場合より遅くなることがある。 一回の順伝播で確かめるトークンの数が、同時のリクエストの数と先読み数に比例して増えるからである1。vLLM に機能を加えたプルリクエストの作成者が H100 一台と Llama 3.1 8B で測ると、TPOT は使わない場合と比べて、バッチが 1 のとき 6.3 ミリ秒から 3.98 ミリ秒に縮み、128 のとき 10.15 ミリ秒から 11.19 ミリ秒に延びた2。そこで vLLM と SGLang は、バッチが大きいほど先読み数を小さくする設定を入れた13。
1 件なら速い先読みが、8 件同時では使わないより遅くなる
※ この節の例は、説明のために作った架空のものです。
投機的デコーディングの速さは、ターゲットモデルの一回の順伝播で進むトークンの数と、その一回にかかる時間の比で決まる。先読みは進むトークンの数を増やす。同時の件数が多いと、その一回にかかる時間も伸びる。この二つを、小さな例で順に数える。
先読みは、一回の順伝播で進むトークンを増やす
「明日の天気は」まで生成した文の続きを、K=3 で生成する。
- ドラフトモデル1. 続きの 3 トークンを予想する:「晴れ」「です」「か」
- ドラフトモデルターゲットモデル2. 予想の 3 トークンを渡し、確かめてもらう
- ターゲットモデル3. 予想を文の後ろにつなげ、一回の順伝播で各位置の次のトークンを自分で選ぶ
- ターゲットモデル4. 「は」の次は「晴れ」、「晴れ」の次は「です」、「です」の次は「。」と選んだ
- ターゲットモデル5. 先頭から照合する。「晴れ」「です」は一致したので受理し、「か」は自分の「。」に置き換える
- ターゲットモデルドラフトモデル6. 確定した「晴れ」「です」「。」を返す。ドラフトモデルはこの後ろから次の予想を始める
ターゲットモデルだけで生成すれば、3 トークンに順伝播が 3 回かかる。この例では 1 回で済んだ。予想が一つも当たらなくても、ターゲットモデルが自分のトークンを 1 個置くので、一回で必ず 1 トークンは進む。3 個とも当たれば、末尾にもう 1 個を足して 4 トークン進む。
予想は 1 トークンずつなのに、確かめるのはまとめてできる。順伝播は、入力に並んだすべての位置について、その位置の次に来るトークンを一度に計算できる。各位置の計算は、その位置より前のトークンだけを見る。ふだんの生成が 1 トークンずつなのは、次の入力がまだ決まっていないからである。
検証では、ドラフトモデルの予想で後ろの入力が先に埋まっている。「明日の天気は晴れですか」を一回の順伝播に入れると、「は」「晴れ」「です」「か」のそれぞれの次が一度に出る。「晴れ」の次の結果は、「明日の天気は晴れ」だけを見て計算される。だから、ターゲットモデルが自分で「晴れ」を選んでから次を計算した場合と同じになる。外れた予想より後ろの結果は、外れた予想を前提にしているので捨てる。ドラフトモデルの予想が 1 トークンずつなのも同じ理由で、小さなモデルなので順に計算しても安い。
図は、各位置で最も確率の高いトークンを選ぶ場合の簡略な例である。確率に従ってトークンを選ぶ場合も、受理の判定に確率を使う規則により、出力はターゲットモデルだけで生成した場合と同じ分布になる4。
同時の件数が多いと、一回の順伝播の時間が伸びる
数を小さくして、次のように置く。
- ターゲットモデルの一回の順伝播は、計算するトークンが 8 個までなら何個でも 1 単位時間かかる。8 個を超えると、時間はトークンの数に比例して伸びる。
- 一件について計算するのは、予想の K 個と末尾に置く 1 個で、K+1 個である。ドラフトモデルの時間は無視する。
- 予想は、一つ前まで当たっていれば 0.7 の確率で当たる。K=3 なら、一件が一回で進むトークンは次の和で、平均約 2.5 個になる。
- ターゲットモデルが必ず置く 1 個:1
- 1 個目の予想が当たる確率:0.7
- 2 個目まで当たる確率:0.7×0.7=0.49
- 3 個目まで当たる確率:0.7×0.7×0.7=約 0.34
各件から見たトークン一つあたりの時間を、二つの場合で比べる。
- 1 件だけ:K=3 なら計算は 4 トークンで、8 個以内なので 1 単位時間のままである。平均 2.5 トークン進むので、トークン一つあたり 1÷2.5=0.4 単位時間になる。使わなければ 1 単位時間なので、2.5 倍速い。
- 8 件が同時:「明日の天気は」「今日の夕飯は」など 8 件を、一回の順伝播でまとめて計算する。使わなければ 8 件×1=8 トークンで 1 単位時間、トークン一つあたり 1 単位時間である。K=3 にすると 8 件×4=32 トークンで 4 単位時間かかる。進むのは同じ平均 2.5 トークンなので、トークン一つあたり 4÷2.5=1.6 単位時間になり、使わない場合より遅い。K=1 なら 8 件×2=16 トークンで 2 単位時間、平均 1.7 トークン進むので 2÷1.7=約 1.2 単位時間である。
この例から言えること
- 計算するトークンが、時間を変えずに計算できる上限(例では 8 個)に収まる間は、先読みは時間を変えずに進むトークンだけを増やすので、速くなる。
- 同時の件数×(K+1)が上限を超えると、時間は K+1 に比例して伸びる。進むトークンは、外れる予想があるので K+1 より少ない。そのため先読みは使わない場合より遅くなり、同時の件数が多いほど小さい K のほうが速い。
確かめるトークンを足しても時間が伸びない間は、先読みで得をする
投機的デコーディングで TPOT が縮むのは、順伝播の時間が GPU のメモリからの読み出しで決まり、確かめるトークンを足しても時間がほとんど伸びない間である。計算の時間がメモリからデータを読み出す速さで決まる状態を、メモリバウンドと呼ぶ。GPU の演算の速さで決まる状態を、コンピュートバウンドと呼ぶ。メモリバウンドの順伝播では、演算器はモデルの重みが届くのを待つ時間が長く、確かめるトークンの計算はその空いた時間に入る。
vLLM の文書は、投機的デコーディングの目的を、毎秒のリクエスト数(QPS)が中程度から低く、メモリバウンドの負荷のときに、トークンの間の時間を縮めることと書く4。vLLM の開発チームは 2024年10月のブログで、QPS が 1 のとき、H100 四台で動かす Llama3-70B のトークンの生成が、対話のデータ(ShareGPT)では最大 1.5 倍、要約のデータ(CNN/DailyMail)では最大 2.8 倍速くなったと報告した5。
同時のリクエストが多いと、固定の先読み数は使わない場合より遅くなる
同時に処理するリクエストが増えると、確かめる計算がコンピュートバウンドの順伝播にそのまま加わり、固定の先読み数では使わない場合より遅くなる。vLLM の文書は、バッチの大きさを BS とすると、検証で実際に計算するバッチの大きさは BS×K になり、これがある大きさを超えると投機的デコーディングは TPOT を悪くすると書く1。同じ開発チームの 2024年のブログも、QPS が高いとき、上と同じ H100 四台の Llama3-70B で ShareGPT では 1.4 倍、CNN/DailyMail では 1.8 倍遅くなったと報告していた5。
2026年に vLLM へ可変の先読み数を加えたプルリクエストの作成者は、バッチの大きさごとの TPOT を次のように測った2。条件は H100 一台、Llama 3.1 8B、MTBench というデータで、ドラフトには、ドラフトモデルの方式の一つである EAGLE を使う。「固定」は先読み数を変えない場合、「可変」はバッチの大きさで変える場合である。
| バッチの | 使わない | 固定 | 可変 |
|---|---|---|---|
| 1 | 6.3 | 3.98 | 3.98 |
| 16 | 6.77 | 4.45 | 4.45 |
| 64 | 7.94 | 6.78 | 6.56 |
| 128 | 10.15 | 11.19 | 9.88 |
| 256 | 16.2 | 19.96 | 17.2 |
バッチが 64 までは、固定の先読み数でも使わない場合より速い。128 になると、固定は 11.19 ミリ秒で、使わない場合の 10.15 ミリ秒より遅い。可変にすると、128 では 9.88 ミリ秒で使わない場合より速く、256 では差が 3.76 ミリ秒から 1.0 ミリ秒に縮んだ。
遅くなり始めるバッチの大きさは、条件で変わる。この表は、作成者がマージの前の版で、一つのモデル、一台の GPU、一つのデータで測った値である2。同じ作成者が InstructCoder というデータで測ると、バッチが 128 のとき固定の先読み数は 11.55 ミリ秒で、使わない場合の 12.69 ミリ秒より速かった2。vLLM の文書も、実際の効果はモデル、トラフィック、ハードウェア、サンプリングの設定で変わると断っている4。
vLLM は利用者が書く表で、SGLang は実行中に観測した受理の長さで先読み数を変える
二つのエンジンは、どちらもバッチが大きいほど先読み数を小さくする。違いは、先読み数を決める根拠である。
vLLM は、GitHub の vllm-project が 2023年から開発する、LLM の推論と配信のためのオープンソースのソフトウェアで、2026年10月18日の時点でスターを 93,987 個集めている6。バッチの大きさで先読み数を変える機能は、2026年6月14日にマージされたプルリクエスト #32374 で入った2。利用者は、バッチの大きさの範囲ごとの先読み数を、設定の num_speculative_tokens_per_batch_size に書く。文書の例は、同時のリクエスト数が 1〜64 なら K=3、65〜128 なら K=1、129〜512 なら K=0 とする1。vLLM は実行中に、そのときの同時のリクエスト数でこの表を引く1。
SGLang は、GitHub の sgl-project が 2024年から開発する、LLM とマルチモーダルモデルの配信のためのオープンソースのソフトウェアで、同じ日の時点でスターを 37,248 個集めている7。SGLang は、一回の検証で受理されたトークンの数(受理の長さ)を見て先読み数を選ぶ。SGLang では、ドラフトモデルが一回の検証の前に予想を重ねる段数(speculative_num_steps)が先読み数に当たる3。この機能を入れたプルリクエスト #21599 は、受理の長さで最適な段数が変わるという測定を動機に挙げる8。受理の長さが平均 6.25 の負荷では段数 7 が最も速かった。平均 2.42 の負荷では、段数 7 は段数 3 にも、使わない場合にも劣った8。
SGLang は、バッチの大きさの範囲ごとに、受理の長さの指数移動平均(新しい観測ほど重く数える平均)を持つ3。検証のたびに平均を更新し、次の段数をおよそ「平均の受理の長さを丸めた値+1」とし、利用者が並べた候補の段数の最小と最大の間に収める3。既定の候補は小さなバッチで 1・3・7 で、バッチが 32 以上なら段数を 1 に固定する3。SGLang の文書は、バッチが大きいと受理されない予想の手間がバッチの全リクエストの分だけ増えるので、最適な段数はバッチが小さいときより小さくなることが多いと書く3。
先読み数を変えても、ドラフトモデルの準備、事前の調整、構成の制限は残る
先読み数を負荷で変えても、使わない場合と同じ速さに戻るとは限らない。利用者は、事前の調整と構成の制限も引き受ける。
一つ目は、K=0 を選んでもドラフトモデルの準備の計算がかかることである。上の測定で、バッチが 256 のとき可変の設定の TPOT は 17.2 ミリ秒で、使わない場合の 16.2 ミリ秒より長かった2。作成者によると、K=0 の間もドラフトモデルは入力のトークンを処理しておく。後でバッチが小さくなったとき、すぐに予想を再開できるようにするためである。作成者は、その分の手間を許容できると書く2。
二つ目は、事前の調整である。vLLM では利用者が範囲ごとの K を自分で決めて書き、SGLang では範囲ごとに候補の段数を並べる13。SGLang の文書は、自分のモデルで段数を固定した場合をバッチの大きさごとに測り、範囲ごとの設定を作るよう勧める。負荷が安定していて一つの固定の設定が十分に調整されていれば、適応型の設定はあまり役に立たないかもしれないとも書く3。モデルや負荷が変わるたびに測り直す手間になる。
三つ目は、使える構成の制限である。vLLM の可変の設定は、EAGLE、EAGLE-3、DFlash で試されている。データ並列(同じモデルの複製を複数の GPU で並べる構成)を 2 以上にすると、複製ごとに違う K を選んで処理の同期が崩れるので、vLLM は可変の設定を自動で無効にし、固定の先読み数に戻す1。SGLang の適応型の設定は、EAGLE か EAGLE3 で、予想を分岐させず一列だけ作る設定(topk=1)のときに限って使え、それ以外では固定の設定に戻る3。どちらの文書も、この制限を外す予定を書いていない。
系列が長ければ、大きなバッチでも先読みで速くなりうる
大きなバッチで先読みが遅くなるかどうかは、系列(入力と生成済みの出力を合わせたトークンの並び)の長さにもよる。ICLR 2025 で発表された MagicDec は、大きなバッチで遅くなるのは非常に短い系列に限られ、ある長さを超えるとバッチが大きくても速くなると報告した9。系列が長いと、過去のトークンについて計算した中間結果(KVキャッシュ)の読み出しが、系列の長さとバッチの大きさとともに増え、順伝播はメモリバウンドに戻りうる9。MagicDec は、KVキャッシュの一部だけを読むドラフトモデルを使い、Llama3.1-8B でバッチが 32〜256 のとき最大 2.51 倍速くなったと報告する9。境目の長さは、モデル、ハードウェア、ドラフトの作り方で変わる9。この報告に従えば、先読み数を決める量はバッチの大きさそのものではなく、順伝播がメモリバウンドかどうかであり、系列の長さもその判定に関わる。
出典9件
-
vLLM project「Dynamic Speculative Decoding」vLLM docs(2026-10-18 取得). https://docs.vllm.ai/en/latest/features/speculative_decoding/dynamic_speculative_decoding/ — 検証のバッチが BS×K になる理由、バッチの範囲ごとの K の設定、データ並列で無効になる制限。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
vLLM project「[V1][Spec Decode] Add Dynamic SD」PR #32374, 2026-06-14 マージ. https://github.com/vllm-project/vllm/pull/32374 — マージ版は範囲ごとの K の表で事前計測はマージ前に外れたこと、作成者の TPOT の測定、K=0 でもかかるドラフトの手間。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
SGLang「Adaptive Speculative Decoding」SGLang docs(2026-10-18 取得). https://docs.sglang.io/docs/advanced_features/adaptive_speculative_decoding — バッチの範囲ごとの受理の長さの指数移動平均で段数を選ぶ設計、既定の設定、EAGLE と topk=1 に限る制限。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9
-
vLLM project「Speculative Decoding」vLLM docs(2026-10-18 取得). https://docs.vllm.ai/en/latest/features/speculative_decoding/ — 中〜低 QPS でメモリバウンドの負荷が対象であること、出力の分布を変えない保証、高 QPS でも EAGLE や MTP の効果を中程度から高いとする定性的な比較表。 ↩ ↩2 ↩3
-
vLLM Team「How Speculative Decoding Boosts vLLM Performance by up to 2.8x」vLLM Blog, 2024. https://blog.vllm.ai/2024/10/17/spec-decode.html — 低 QPS で最大 1.5 倍・2.8 倍、高 QPS で 1.4 倍・1.8 倍遅くなった測定と、負荷で先読みを短くする計画。 ↩ ↩2
-
vllm-project「vllm」GitHub(2026-10-18 取得). https://github.com/vllm-project/vllm — スター数 93,987 とリポジトリの作成年月 2023-02。 ↩
-
sgl-project「sglang」GitHub(2026-10-18 取得). https://github.com/sgl-project/sglang — スター数 37,248 とリポジトリの作成年月 2024-01。 ↩
-
SGLang「[SPEC][1/N] feat: add adaptive speculative_num_steps for EAGLE topk=1」PR #21599, 2026-04-20 マージ. https://github.com/sgl-project/sglang/pull/21599 — 受理の長さが違う負荷で固定の段数の最適値が違うという動機の測定。 ↩ ↩2
-
Sadhukhan ほか「MagicDec: Breaking the Latency-Throughput Tradeoff for Long Context Generation with Speculative Decoding」ICLR, 2025. https://arxiv.org/abs/2408.11049 — 長い系列ではバッチ 32〜256 でも速くなるという報告。 ↩ ↩2 ↩3 ↩4
この記事はAIを使用して執筆されました。内容には誤りが含まれる可能性があります。事実や数字の出典は脚注から確かめられます。