Tech カテゴリのイメージ

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は次の順序で進む。

  1. routerがPrefill workerを選ぶ
  2. Prefill workerがpromptを処理してKV Cacheを作る
  3. KV CacheをDecode workerへ転送する
  4. 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
Routerrouting時間、候補なし、fallback、version不一致
Prefillqueue age、input tokens/s、TTFT内のPrefill時間、cache hit
TransferKV byte、待機時間、転送時間、帯域、失敗率
Decoderunning 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で比較する。

  1. 短いprompt・短いoutput
  2. 長いprompt・短いoutput
  3. 短いprompt・長いoutput
  4. 長いprompt・長いoutput
  5. 平常traffic中への長文burst
  6. prefix cache hitとmissの混在
  7. KV転送帯域を絞った状態
  8. PrefillまたはDecode worker障害
  9. 新旧versionのrollout
  10. 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を観測できるかで決まる。

参考資料