SGLangにおけるEPDデカップリング:ビジョン言語モデルの弾性エンコーダー拡張

TL;DR

SGLangがEncoder-Prefill-Decode (EPD)デカップリングアーキテクチャを発表し、視覚言語モデル(VLMs)における視覚エンコーディングと言語処理を分離し、以下の利点を実現:

  • 視覚エンコーディング容量の独立拡張:エンコーダーサーバーは水平スケーリングが可能で、言語モデルのデプロイメントに影響を与えず、視覚集約型負荷のリソース利用を最適化。
  • 既存のPDデカップリングとの互換性:EPDはPrefill-Decodeデカップリングと組み合わせて、完全な3層アーキテクチャを形成。
  • 柔軟な転送バックエンド:ZMQ、Mooncakeなど複数の転送メカニズムをサポートし、異なるデプロイメントシナリオに対応。
  • 視覚埋め込みキャッシュ:よく使用される画像はエンコーダーサーバーでキャッシュでき、重複するViT計算を回避し、ネットワーク転送のオーバーヘッドを削減。

EPDは画像密集シナリオ(複数画像入力など)で効果が顕著で、視覚エンコーディングが主なボトルネック。EPDにより、負荷下でリクエストのTTFTが大幅に削減され、同一配置ソリューションと比較して遅延が約6〜8倍低下(1 QPSの場合)。一方、画像疎シナリオでは、追加のネットワーク遅延によりTTFTが高くなる可能性がある。

はじめに

Qwen2.5-VLやLlama-Visionなどの視覚言語モデル(VLMs)は視覚理解と言語生成を融合させているが、独特のスケーリング課題に直面している:

  • 異種計算要求:視覚エンコーディング(CNN/ViT)と言語デコーディング(Transformer)の計算パターンが異なる。
  • リソース使用の不均衡:視覚処理は計算集約的だが、prefillフェーズでのみ必要。
  • 柔軟性の不足:従来のモノリシックデプロイメントでは視覚と言語コンポーネントを独立してスケーリングできない。
  • リクエスト内並列性:同じリクエスト内の異なる画像は独立してエンコード可能。
  • テンソル並列スケーリングの不良:視覚エンコーダーのパラメータは言語コンポーネントよりはるかに少なく、テンソル並列は非効率で不要。

SGLangの既存のPrefill-Decode(PD)デカップリングはすでにprefillとdecodeフェーズを分離している。EPDはさらに視覚エンコーディングと言語prefillを分離し、3層アーキテクチャを形成。

ViTスケーリング問題:なぜテンソル並列が常に有効ではないのか

直感に反する発見

EPDの重要な洞察は、Vision Transformers(ViT)がテンソル並列度(TP)の増加から恩恵を受けないことで、TPが高いほど遅くなる可能性すらある:

H20上でのQwen2.5-VL-72Bベンチマーク(リクエストあたり4画像):

TPViT平均時間
2492.13ms
4465.80ms
8523.80ms

理由:

  1. 通信オーバーヘッドが実行時間を支配。
  2. 視覚モデルの重みパラメータは通常小さい。

EPDはTPを増やすのではなく、エンコーダーの水平スケーリングによってこの問題を回避。

アーキテクチャ概要

EPDアーキテクチャのリクエストフロー:

  1. クライアントリクエスト:マルチモーダルリクエストがprefillサーバーに到達(ロードバランサー経由または直接接続)。
  2. 画像配信:prefillサーバーが画像入力を識別し、1つ以上のエンコーダーサーバーに配信。画像は負荷分散のため分割可能。
  3. 視覚エンコーディング:エンコーダーサーバーがViTで画像を処理し、視覚埋め込みと画像グリッドメタデータを生成。有効な場合は結果をキャッシュ。
  4. 埋め込み転送:視覚埋め込みは設定された転送バックエンド(ZMQ、Mooncakeなど)経由でprefillサーバーに返送。
  5. LLM計算:prefillサーバーが視覚埋め込みとテキストトークンを結合し、事前計算テンソルを含むmm_inputsを形成。LLMがPrefillとDecodeを実行。PDが有効な場合は既存の転送ロジックを再利用;そうでない場合はローカルでデコード。

主要コンポーネント

EPD Workflow

EPD Architecture

エンコーダーサーバー (--encoder-only)
- 視覚のみ(言語重みなし);前処理 + ViTフォワードで視覚埋め込みを生成
- プレフィックスマルチモーダルキャッシュをサポート
- 負荷分散と複数画像並列分割推論のための水平スケーリング

Prefillサーバー (--language-only)
- 言語モデルのみ
- エンコーダー埋め込みを受信
- PD有効:DecodeにKVを送信;そうでない場合はローカルデコード

Decodeサーバー
- 標準的なdecode専用インスタンス
- prefillからKVキャッシュを受信

実装詳細

画像配信戦略

単一モデルを分割するテンソル並列とは異なり、EPDはデータ並列を使用:複数の独立したエンコーダーインスタンスを実行し、画像を配信。

例(7画像、3エンコーダー):

Request with 7 images: [img0, img1, img2, img3, img4, img5, img6]
3 encoders available

Distribution (after shuffle):
├─ Encoder 0: [img0, img1, img2] (3 images)
├─ Encoder 1: [img3, img4] (2 images)
└─ Encoder 2: [img5, img6] (2 images)

転送バックエンド

EPDは3つの視覚埋め込み転送バックエンドをサポート:

  • zmq_to_scheduler (デフォルト):直接ZMQソケット通信、RDMA転送エンジン経由で埋め込みをスケジューラーに送信、非ブロッキング。
  • zmq_to_tokenizer:埋め込みをtokenizer managerに送信、トークン化段階で処理。
  • mooncake:マルチノードRDMA転送、共有メモリに埋め込みを登録、高帯域幅低遅延。

視覚埋め込みキャッシュ

エンコーダーはプレフィックスマルチモーダルキャッシュをサポートし、重複するViT計算を回避:

  • 冗長な視覚エンコーディングを削除
  • 繰り返し画像の遅延を削減
  • 設定可能なキャッシュサイズ(デフォルト4GB、SGLANG_VLM_CACHE_SIZE_MB経由)

使用例

エンコーダーインスタンスの起動:

MODEL=Qwen/Qwen2.5-VL-7B-Instruct
PORT=30002

CUDA_VISIBLE_DEVICES=2 taskset -c $1 python -m sglang.launch_server \
    --model-path $MODEL \
    --encoder-only \
    --enable-prefix-mm-cache \
    --port $PORT

prefillインスタンスの起動:

MODEL=Qwen/Qwen2.5-VL-7B-Instruct
PORT=30000
TP=1
MEM_FRACTION=0.5
CHUNK_SIZE=8192

SGLANG_VLM_CACHE_SIZE_MB=0 CUDA_VISIBLE_DEVICES=0 python -m sglang.launch_server \
    --model-path $MODEL \
    --disaggregation-mode prefill \
    --disaggregation-transfer-backend nixl \
    --tp $TP \
    --mem-fraction-static $MEM_FRACTION \
    --disable-radix-cache \
    --chunked-prefill-size $CHUNK_SIZE \
    --language-only \
    --encoder-urls http://127.0.0.1:30002 http://127.0.0.1:30003 http://127.0.0.1:30004 http://127.0.0.1:30005 http://127.0.0.1:30006 http://127.0.0.1:30007 \
    --port $PORT

decodeインスタンスの起動:

MODEL=Qwen/Qwen2.5-VL-7B-Instruct
PORT=30001
TP=1

CUDA_VISIBLE_DEVICES=1 python -m sglang.launch_server \
    --model-path $MODEL \
    --disaggregation-mode decode \
    --disaggregation-transfer-backend nixl \
    --tp $TP \
    --port $PORT

minlbの起動:

python -m sglang_router.launch_router \
  --pd-disaggregation \
  --mini-lb \
  --prefill http://127.0.0.1:30000 \
  --decode http://127.0.0.1:30001 \
  --port 8000

ベンチマーク

EPDは視覚集約型負荷(複数画像リクエスト)を対象とし、エンコーダーの水平スケーリングによってTTFTを向上。

ベンチマークスクリプト:

python -m sglang.bench_serving \
    --random-image-count \
    --model ${MODEL_PATH} \
    --num-prompts 64 \
    --dataset-name image \
    --random-input-len 128 \
    --random-output-len 256 \
    --image-count 8 \
    --image-resolution 1080p \
    --host $HOST_IP \
    --port $port \
    --backend vllm-chat \
    --request-rate $request_rate

実験設定

環境:8× H20 96GB GPU

モデル:Qwen3-VL-235B-A22B-Instruct-FP8

データセット:ランダムマルチモーダルデータセット
- テキストトークン:128 / 256
- リクエストあたり画像:1-8枚(ランダム、平均 ~4枚)
- 画像解像度:1080p
- QPS範囲:0.2-1.0

デプロイメント設定
- Colocate:1 PDインスタンス、tensor-parallel-size=4、4× H20使用
- 1E1P:1エンコーダー (TP=1) + 1 PD (TP=4)、5× H20使用
- 2E1P:2エンコーダー (各TP=1) + 1 PD (TP=4)、6× H20使用

テスト結果

平均TTFT (EPD vs colocate):

TTFT Results

平均TPOT (EPD vs colocate):

TPOT Results

リクエストスループット (EPD vs colocate):

Throughput Results

主要な発見(vs. colocate):

  • 負荷下でエンコーダー/prefillはTTFTをcolocateよりはるかに低く維持(1 QPSで≈6〜8倍低い)。
  • TPOTはcolocateよりはるかに低い(≈8〜10倍低い)、遅延がよりコンパクト。
  • 高QPSでスループットが約2倍(0.8〜1.0 QPSで≈2倍)。
  • エンコーダーに専用GPUリソースを割り当てることで、TTFTの劇的な削減を実現。2E1Pは50%多くのGPU(6× vs 4×)を使用するが、より高いリソース利用率を達成。