TT Lab
ブログ

한국어 English 日本語 中文

はじめる
ブログ

ブログ

並列化、マルチモーダル、プーリング:GPU 1基で測定できるものとできないもの

ステータス:ドラフト(2026-10-27)、プーリング・マルチモーダル実測、並列化未測定。 プーリングの測定は8GBクラスのノートPC用GPU(Ada世代)1基、vLLM 0.31.0を使用して2026-10-27に行いました。並列化とマルチモーダルは未測定であり、公式ドキュメント(0.32.0)および論文で確認できた内容のみを記載しています。LoRAマルチサービングの実測は「トス ML/AI Infra 準備」の第15話にあります。 2026-10-27 追加。 承認を受けてvLLM 0.32.0イメージとQwen3-VL-2B-Instruct(Apache-2.0、safetensors 4.26GB)を取得し、セクション2-1のマルチモーダル実測を追加しました。初版では「未測定」だった内容です。 執筆ルール:実測データを基本とし、測定できなかったものは「未測定」、確認できなかったものは「未確認」と表示します。

目次

1. 4つの並列化:何を分割するのか

方式 オプション 分割するもの 通信 使用するタイミング
テンソル並列(TP) --tensor-parallel-size 1つの層の行列をGPUごとに分割 層ごとにall-reduce モデルがGPU 1基に収まらない場合、1つのサーバー内
パイプライン並列(PP) --pipeline-parallel-size 層を前後に分割し、GPUごとに数層ずつ ステージ間の活性値のみ 複数サーバーにまたがる場合、NVLinkがない場合
データ並列(DP) --data-parallel-size 分割せずモデルを丸ごと複数コピー ほぼなし(MoEにはあり) スループットを向上させる場合
エキスパート並列(EP) --enable-expert-parallel MoEのエキスパートをGPUごとに トークンをエキスパート側にall-to-all 大規模なMoEモデル

モデルが収まるか確認する際は、起動ログの2行を確認します。 GPU KV cache size: N tokens と Maximum concurrency for M tokens per request: Kx です。このシリーズの実験サーバーは「40,208トークン、4,096トークンリクエスト基準で9.82倍」でした。この並行性が目標より低い場合、GPUを増やすようドキュメントで案内されています。

GPUが1基であるため、4つの方式すべて未測定です。同系統のGPU 2基を1GbEで接続してKVを転送した際の実測(分散サービングが20倍以上遅かった)が「トス ML/AI Infra 準備」の第24話にあるため、低速な接続でGPUを接続する際にかかるコストについては、その記事で代わりとします。

2. マルチモーダル入力

vLLMは画像・動画・音声を処理するモデルを、同じOpenAI互換APIで提供します。チャットメッセージの content に image_url, video_url, input_audio の断片を挿入すると、APIサーバーのプロセッサがメディアをダウンロードして前処理を行い、モデルのエンコーダーに送信します。画像1つはプロンプト内で複数のプレースホルダー・トークンに変換され、プレフィックスキャッシュはこのプレースホルダーブロックのハッシュに画像のハッシュを混ぜ合わせ、同じ画像同士でのみキャッシュを共有します(第2話のプレフィックスキャッシュ設計ドキュメント)。

運用においてパフォーマンスよりも先に対処すべきなのはこちらです。

初版ではビジョン言語モデルを新しく取得する必要があったため未測定でしたが、承認後に以下のセクション2-1で測定しました。

2-1. 2026-10-27の実測:8GB環境で2Bのビジョン言語モデルを動かす

まず、起動させる

設定 KVの残りメモリ KVの空き 結果
デフォルト(--gpu-memory-utilization 0.85) -0.07GiB 起動失敗 「No available memory for the cache blocks」
--limit-mm-per-prompt '{"image":1,"video":0}' のみ追加 -0.07GiB 同様に失敗
メモリ比率 0.92 0.47GiB 4,352トークン(4,096トークンリクエスト1.06個分) 起動成功
0.92 + サイズヒント {"image":{"count":1,"width":896,"height":896},"video":0} 2.13GiB 19,904トークン(4.86個分) 起動成功

vLLMは起動時に「モデルが受け取ることのできる最大サイズ画像」でエンコーダーを1回実行し、メモリを測定します。ログには、エンコーダーキャッシュの予算を16,384トークンに設定したと記録されています。重み4GBにその予算まで加わると、8GB環境ではKVの空き領域が残りませんでした。画像の枚数を減らすだけでは意味がなく、実際に受け取る画像のサイズを教えることで、KVが4.5倍になりました。小型GPUでビジョン言語モデルを起動する際に真っ先に調整すべきパラメータです。

結果(サイズヒントサーバー)

項目 448×448 896×896
プロンプトトークン(画像 + 質問) 220 808
新規画像のTTFT中央値 67.9ms 238.0ms
同じ画像、同じ質問を再度 31.8ms(キャッシュ208トークン) 32.7ms(キャッシュ800トークン)
同じ画像、異なる質問 32.1ms(キャッシュ192トークン) 33.4ms(キャッシュ784トークン)

SSRF:サーバーが代わりにダウンロードするか

リクエストの画像アドレス デフォルトサーバー --allowed-media-domains example.com
http://127.0.0.1:8000/health(サーバー自身) 400 「Failed to load image: cannot identify image file」(5.5ms) 400 「The URL must be from one of the allowed domains」(2.6ms)
http://127.0.0.1:8000/metrics 400、同じメッセージ 400、同じ拒否
file:///etc/passwd 500 「Cannot load local files without --allowed-local-media-path」 同上

デフォルトサーバーのメッセージは「画像ではない」です。すなわち、 サーバーがその内部アドレスに対して実際にリクエストを送信し、レスポンスを受け取ってきたことを意味します。レスポンスの内容は返されませんでしたが、リクエスト元はこのサーバーを介して内部ネットワークを叩き、応答時間やエラーから何が公開されているかを調べることができます。ドメインを指定すると、受け取る前に拒否されました。ローカルファイルはデフォルトでブロックされていました。画像URLを受け取るサービスであれば、 --allowed-media-domains は選択ではなく必須です。

3. プーリング:埋め込みと再ランク付けも同じエンジンで

生成を行わず、最後の隠れ状態を取得して使用するモデルをvLLMでは プーリングモデルと呼びます。 --runner pooling で起動すると、生成の代わりに /v1/embeddings, /score, /rerank, /classify, /pooling を提供します。因果モデルは最後のトークン(LAST)、BERT系モデルは最初のトークン(CLS)をデフォルトで使用します。公式ドキュメントには、「プーリングモデルのサポートは利便性のためのものであり、他の方式より速いという保証はない」とあらかじめ記載されています。

方法

結果

タスク 実行したリクエスト レイテンシ中央値(5回) スループット 備考
埋め込み 文64個 × 約127トークン 371.8ms 秒間172個(約21,900トークン) 次元 1,024
再ランキング 質問1 + 文書32個 33.9ms 秒間944個 一致する文書が 0.993 で1位

4. LoRAマルチサービング

ベースモデル1つにLoRAアダプターを複数載せ、リクエストごとに異なるアダプターを使用する機能です(--enable-lora --max-loras --max-lora-rank)。同じ8GB級GPUで1.7Bモデルにより測定した値はLoRAアダプターサービングにあります。アダプターを使用すると8.5〜17.9%遅くなり、アダプター数がスロット数を超えるとスループットが大幅に低下しました。今回の記事で追加する内容は2つあります。

5. 検証できなかった事項

参考

ログインするといいねできます

コメント

まだコメントはありません。

ログインするとコメントできます