Prefill・Decode分離とは?LLM推論を二つのGPUプールで処理する理由
長い資料を入力した利用者が現れると、すでに回答を受け取っていた別の利用者のtoken配信まで遅くなることがある。同じGPUで、長いpromptを一気に読むPrefillと、1 tokenずつ回答を生成するDecodeが計算資源を奪い合うためである。
Prefill・Decode分離は、この二つを別のworker poolへ置く。長文promptの処理がDecodeへ割り込みにくくなり、Time to First Tokenとtoken配信間隔を別々に調整できる。一方で、Prefillが作ったKV CacheをDecode側へ転送するnetworkと運用の複雑さが増える。
LLM推論には性質の違う二つの段階がある
Prefillでは、利用者のprompt全体をmodelへ通し、各tokenに対応するKeyとValueを計算してKV Cacheへ保存する。長いpromptほど計算量が増え、GPUの演算性能が効きやすい。
Decodeでは、保存したKV Cacheを使いながら次のtokenを一つずつ生成する。各stepで過去のcontextを参照するためmemory accessが多く、同時request数とoutput長、memory帯域、KV Cache容量が効きやすい。
一般的な一体型構成では、一つのworkerがPrefillとDecodeを両方処理する。構成は単純だが、長いPrefillが入ると進行中Decodeの次stepが待たされる。利用者には、streaming中のtokenが一時的に止まったように見える。
分離するとrequestは二つのworkerを通る
Prefill・Decode分離では、requestは次の順序で進む。
- routerがPrefill workerを選ぶ
- Prefill workerがpromptを処理してKV Cacheを作る
- KV CacheをDecode workerへ転送する
- Decode workerがtokenを生成し、利用者へstreamingする
NVIDIA Dynamoの公式資料は、Prefill poolとDecode poolを別々のGPU上へ置き、それぞれを独立してsize、配置、scaleできる構成として説明している。
一体型が一つのworkerで完結するのに対し、分離型ではrouter、二種類のworker、KV Cache転送が必要になる。単にPodを二種類作るだけではない。
分離の目的は二つのlatencyを別々に守ること
Prefillの利用者体験は主にTime to First Token、つまり最初のtokenが返るまでの時間へ現れる。Decodeの体験は、token同士の間隔であるInter-Token LatencyやTime per Output Tokenへ現れる。
一体型ではPrefillとDecodeが同じresource allocationとparallelismを共有する。Prefillを優先すれば進行中のDecodeが遅れ、Decodeを優先すれば新しいrequestのTTFTが伸びる。
OSDI 2024で発表されたDistServe論文は、この干渉とresource allocationの結合を問題としてPrefillとDecodeを別GPUへ分けた。評価では、90%以上のrequestがlatency制約を満たす条件で、当時の比較対象より最大7.4倍多いrequestを処理、または最大12.6倍厳しいSLOを満たしたと報告している。これは特定のmodel、hardware、workload、比較条件での研究結果であり、すべての環境で同じ倍率になるという意味ではない。
PrefillとDecodeを独立してscaleできる
trafficの形によって、二つの段階に必要な台数は変わる。
- RAGで長い資料を毎回渡すならPrefill負荷が増える
- 短い質問から長文を生成するならDecode負荷が増える
- 同時接続が増えるとDecode側のKV Cacheとmemory帯域が圧迫される
- prefix cacheが効けばPrefillの再計算量が減る
分離型なら、input token backlogとPrefill queueが増えたときPrefill workerを増やし、running sequenceとoutput token backlogが増えたときDecode workerを増やせる。Autoscalingのsignalも段階ごとに選べる。
ただし、一方だけを増やしてもend-to-end throughputが上がるとは限らない。Prefillを増やしすぎれば、完成したKV CacheがDecode待ちになる。Decodeを増やしてもPrefillが供給できなければGPUが余る。routerは二つのqueueと転送帯域を見て組を選ぶ必要がある。
最大の追加費用はKV Cache転送である
Prefillが作ったKV Cacheは、Decodeに必要な状態である。別workerへ分ければ、この状態をGPU間またはnode間で移動しなければならない。contextが長く、modelのlayer数やhidden dimensionが大きいほど転送量も増える。
同一node内ならNVLinkなどの高速接続を使える場合がある。nodeをまたぐならRDMAのような高速networkが重要になる。llm-dのarchitecture資料は、効率的なKV転送に高性能なRDMA接続が必要で、TCP fallbackはtestや開発向けとしている。
転送時間がPrefillとDecodeの干渉削減より大きければ、分離の利点は消える。次を測る必要がある。
- requestごとのKV Cache byte数
- 転送開始までの待ち時間
- 転送時間と実効帯域
- 転送失敗・timeout・再試行
- 転送後にDecodeが始まるまでのqueue時間
- topology別のTTFTとITL
GPU utilizationだけでは、networkがbottleneckになった状態を見抜けない。
すべてのrequestを分離する必要はない
短いpromptでは、KV Cacheを別workerへ移す固定費の方が大きくなる場合がある。Decode workerに空きがあり、そのworkerがPrefillも実行できるなら、一体型で完結した方が速いこともある。
llm-d Routerの資料には、cacheされていないprefixの長さがthresholdを超えるときだけremote Prefillを選ぶ仕組みがある。つまり、requestごとに分離するかを決めるhybridな運用も可能である。
判断材料には次がある。
- cacheされていないinput token数
- Prefill poolとDecode poolのqueue
- Decode worker上のprefix cache hit
- KV転送量とnetwork topology
- requestのTTFT・ITL SLO
- modelとtenantの配置制約
「分離型を導入したから全件分離」と固定せず、短いrequestやcache hitはDecode側だけで処理する余地を残す。
routerが新しい制御点になる
分離前は、routerがmodelとworkerを一つ選べばよかった。分離後は、互換性のあるPrefill workerとDecode workerを組み合わせる。
model名が同じでも、weight version、tokenizer、KV Cache形式、parallelism、quantizationが一致しなければ状態を安全に受け渡せない。rollout中に新旧versionを混ぜると、転送後にDecodeできない可能性がある。
routerには少なくとも次が必要になる。
- modelとversionの互換性検査
- 段階別のqueueとtoken loadを使うrouting
- topologyを考慮したworker選択
- Prefill失敗時のfallback
- client cancelとdeadlineの両段階への伝播
- rollout時のPrefill・Decode構成の同期
NVIDIA Dynamoも、rackやzoneをまたぐ場合はKV転送domainを考慮してDecode routingを制約または優先するよう案内している。
Scale to Zeroとの組み合わせ
PrefillとDecodeを独立して縮退させられることは利点だが、両方を0にすると復帰経路が長くなる。Prefillを起動してKV Cacheを作った後、Decode workerも起動するなら、最初のrequestは二段階のcold startを受ける。
対話SLOが短い場合は、Decode poolを最低1以上で温存し、burst性の高いPrefillだけをscaleする構成が考えられる。逆に夜間batchだけなら両方を0へ落とし、queueで待たせてもよい。段階ごとの起動時間と費用を測り、一体型のScale to Zeroとも比較する。
監視を段階別に分ける
end-to-end latencyだけでは原因を特定できない。次をPrefill、transfer、Decodeへ分解する。
| 段階 | 主なmetric |
|---|---|
| Router | routing時間、候補なし、fallback、version不一致 |
| Prefill | queue age、input tokens/s、TTFT内のPrefill時間、cache hit |
| Transfer | KV byte、待機時間、転送時間、帯域、失敗率 |
| Decode | running sequence、output tokens/s、ITL、KV Cache使用率 |
| 全体 | TTFT、ITL、E2E latency、SLO内で完了したrequest率 |
DistServeは単なる最大throughputではなく、TTFTとTPOTの両方をSLO内に保って処理できるrequest rateをgoodputとして評価した。運用でも、tokens/sだけを上げてtail latencyを悪化させない指標が必要である。
負荷試験で比較する条件
分離型は構成が複雑なため、理論だけで採用しない。一体型をbaselineとして同じmodel、hardware総数、request traceで比較する。
- 短いprompt・短いoutput
- 長いprompt・短いoutput
- 短いprompt・長いoutput
- 長いprompt・長いoutput
- 平常traffic中への長文burst
- prefix cache hitとmissの混在
- KV転送帯域を絞った状態
- PrefillまたはDecode worker障害
- 新旧versionのrollout
- client cancelとdeadline超過
平均だけでなく、TTFTとITLのp95・p99、SLOを満たしたrequest率、GPU時間、network byteを比較する。
分離は高負荷・長文環境で効く設計選択である
Prefill・Decode分離は、演算中心のPrefillとmemory帯域中心のDecodeを別GPU poolへ置き、互いの干渉を減らす。二つを独立してscale・parallelizeできるため、長いpromptと多い同時生成が混在する環境でSLOを守りやすくなる。
一方、小さいmodel、短いprompt、低い同時実行数、高速なKV転送経路がない環境では、一体型の方が単純で速い可能性がある。分離の成否はworker数ではなく、KV Cacheをどれだけ速く安全に渡せるか、requestごとに分離の必要性を判断できるか、段階別のqueueを観測できるかで決まる。