vLLM-Omni による Qwen3-Omni-30B-A3B-Instruct サービングの最適化

vLLM-Omni による Qwen3-Omni-30B-A3B-Instruct サービングの最適化

TL;DR

vLLM-Omni は Qwen3-Omni-30B-A3B-Instruct を Thinker、Talker、Code2Wav からなるステージ化されたパイプラインとしてサービングし、ステージレベルのバッチ処理、CUDA Graph キャプチャ、非同期チャンクハンドオフ、非同期出力、Talker/Code2Wav 用のステージレプリカ、およびホットパスのクリーンアップを適用することで、オンラインサービングのパフォーマンスを向上させます。その結果、リクエストスループットの向上、オーディオの TTFP(Time-to-First-Packet)の低減、およびリアルタイムファクター(RTF)の低減を実現しています。

vLLM-Omni における Qwen3-Omni パイプライン

Qwen3-Omni はマルチモーダル理解と音声生成を組み合わせており、vLLM-Omni では 3 ステージのデータフローとしてサービングされます。Thinker がマルチモーダル推論とテキスト生成を行い、Talker が隠れ状態を RVQ コーデックコードに変換し、Code2Wav がそれらのコードから波形オーディオを再構成します。このパイプラインは OpenAI 互換の /v1/chat/completions エンドポイントを使用し、リクエストボディの modalities フィールドで ["text" ]["text", "audio" ] といった出力タイプを指定します。デフォルトのデプロイプロファイルは --omni で起動すると自動的に選択されます。明示的なプロファイルは --deploy-config vllm_omni/deploy/qwen3_omni_moe.yaml を介して指定できます。vLLM-Omni はプロファイルの platforms: セクションからプラットフォーム固有の差分をマージするため、同じ起動コマンドが CUDA、NPU、ROCm、および XPU バックエンドで動作します。

最適化技術

ステージ分解とバッチ処理

ステージ分解は、Thinker、Talker、Code2Wav を独立したサービングオブジェクトに分離します。これにより、各ステージが単一のポリシーを共有して最も遅いサブパスが全体を制限されるのではなく、各ステージが独自のバッチ処理、グラフ、およびデバイスポリシーを持つことが可能になります。ステージごとのバッチ処理は、並行するリクエストを 1 つの Talker MTP 呼び出しと 1 つの Code2Wav フォワードにグループ化し、単一リクエストのマイクロワークによってアイドル状態になる SM を埋め、固定のステップあたりのコストをバッチ全体で分散させます。このバッチ化されたステージ分解構成が、その後のすべての最適化のベースラインとなります。

CUDA Graph キャプチャ

CUDA Graph は、固定されたオペレータシーケンスを一度キャプチャして最小限の CPU ワークで再生することで、デコードの各ステップにおける繰り返しの CPU 側カーネルディスパッチを排除します。Thinker と Talker は vLLM の外部 CUDA Graph パスを使用します。Talker の内部コード予測器は torch.compile で最適化されています(競合を避けるため 2 層目のグラフはなし)。Code2Wav は、コネクタ設定の codec_chunk_frames および codec_left_context_frames に基づいて形状をキャプチャする内部 CUDAGraphDecoderWrapper を使用し、SnakeBeta キャッシュを事前計算し、ラッパーのバッチまたはチャンクデコードエントリポイントを介してチャンクをディスパッチします。3 つのステージすべてで CUDA Graph を有効にすると、リクエストスループットは 2.2 から 8.6 req/s (+299%) に向上し、平均オーディオ TTFP は 5884 ms から 2790 ms (−53%) に短縮され、コンカレンシー 64 において平均オーディオ RTF は 1.15 から 0.59 (−49%) に減少します。

非同期チャンクハンドオフ

非同期チャンクは、フルペイロードのステージバリアをパイプライン化された部分的なハンドオフに置き換えます。Thinker は埋め込み行をインクリメンタルに放出します。Talker はコーデックフレームを蓄積し、initial_codec_chunk_frames / codec_chunk_frames の境界でスライスします。非同期スケジューラはチャンク転送とステージ計算をオーバーラップさせ、前のステージがまだデコードしている間に各ステージが作業を開始できるようにします。この変更により、オーディオ TTFP は最大の単一削減を実現し、コンカレンシー 64 においてリクエストスループットが 8.6 から 9.3 req/s (+8%) へと緩やかに増加する一方で、平均オーディオ TTFP は 2790 ms (CUDA Graph) から 655 ms (−77%) へと低下します。平均オーディオ RTF は 0.63 のままです。

非同期出力

非同期出力は、Thinker コネクタペイロードの組み立てを非ブロッキングパスに移動することで、ペイロード構築をステージハンドオフから切り離し、デコードワーカーが埋め込みや隠れ状態の同期コピーによって停止しないようにします。非同期チャンクが既に有効な状態で、非同期出力は平均オーディオ TTFP を 631 ms 付近に維持し(非同期チャンクから −4%)、コンカレンシー 64 において平均オーディオ RTF を 0.63 から 0.47 (−25%) に下げ、リクエストスループットを 9.3 から 11.3 req/s (+22%) に向上させます。

ステージレプリカ

ステージレプリカは、負荷がかかったときに飽和するステージにのみ容量を追加します。各リクエストは数百回の Talker デコードステップと Code2Wav ヴォコーダーフォワードを必要としますが、Thinker の生成は 1 回のみであるため、Talker と Code2Wwav が最初にボトルネックになります。GPU 0 に単一の Thinker を保持し、GPU 1 と 2 に 2× Talker と 2× Code2Wav レプリカをデプロイすることで、大規模なマルチモーダル Thinker を複製することなく、音声側のバックログを吸収できます。非同期出力に加えてレプリカを追加すると、コンカレンシー 64 においてリクエストスループットは 11.7 req/s (+4%) に上昇し、平均オーディオ TTFP は 632 ms 付近、平均オーディオ RTF は 0.47 を維持します。

ホットパスのクリーンアップ

ホットパスのクリーンアップは、Talker デコードループとコネクタペイロードにおける発話の長さに比例してスケールするステップごとのオーバーヘッドを排除します。変更内容には以下が含まれます:初期プリフィル後の新しい embed.decode 行のみの送信(ステップあたりのコネクタトラフィックは O(1))、マルチプロセスオーバーヘッドを避けるためのデフォルトでの単一 GPU uni エグゼキュータの使用、ペイロード構築における繰り返しの torch.cat の排除、SDPA、ネイティブ GQA、インライン top-k サンプリング、キャッシュされたモジュール参照、および torch.compile(競合する 2 層目のグラフなし)を使用した Talker コード予測器の書き換え、model_intermediate_buffer 内での中間テンソルの GPU 滞留の維持、冗長なデバイス・ホスト間読み取りのスキップ、および不要なマルチモーダル位置計算の回避。長コンテキストの単一リクエストテストでは、これらの変更により、エンドツーエンドのレイテンシが 21.28 s から 7.37 s に、オーディオ TTFP が 3197 ms から 1796 ms に、オーディオ RTF が 0.71 から 0.28 に減少しました。これらの利点は上記の最適化と積み重なり、DFX パフォーマンススイートのベースラインに反映されています。

検証結果

検証では、モデル Qwen3-Omni-30B-A3B-Instruct、プロンプト長 10/160/320/640 トークン、コンカレンシーレベル 1/16/32/64、5 回のウォームアップ、および 0/1/2 にマッピングされた 3 つの可視 GPU を使用して、Seed-TTS en で制御されたベンチマークスイープを行いました。各構成は、分離されたデプロイプロファイルでサーバーを再起動し、前の行の最適化に 1 つの最適化を追加しました。Batch から Async output までの構成では、GPU ごとに 1 つのステージを固定しました(GPU 0/1/2 に Thinker/Talker/Code2Wav、各 1 レプリカ)。Stage replicas の行では、Thinker を GPU 0 に保持し、GPU 1 と 2 で 2× Talker + 2× Code2Wav を実行しました。

コンカレンシー 64 の場合:

  • Batch baseline: 2.2 req/s, 平均オーディオ TTFP 5884 ms, 平均オーディオ RTF 1.15
    • CUDA Graph: 8.6 req/s (+299%), TTFP 2790 ms (−53%), RTF 0.59 (−49%)
    • Async chunk: 9.3 req/s (+8%), TTFP 655 ms (−77%), RTF 0.63
    • Async output: 11.3 req/s (+22%), TTFP 631 ms (−4%), RTF 0.47 (−25%)
    • Stage replicas: 11.7 req/s (+4%), TTFP 632 ms, RTF 0.47

スループット(図 7)は、コンカレンシー 64 においてリクエストスループットが Batch baseline の 2.2 req/s から 11.7 req/s (5.4×) へ、コンカレンシー 32 において 1.1 から 6.8 req/s へ上昇することを示しています。最大の単一の跳躍は CUDA Graph (4×) です。非同期出力は高コンカレンシーにおける最後の後押しを提供し、ステージレプリカはコンカレンシーが増加するにつれて拡大するヘッドルームとともにピークスループットを提供します。

リアルタイムファクター(図 8)は、Batch の 1.15(リアルタイム以上)から、コンカレンシー 64 では 0.47 に低下しており、負荷がかかるとデコードが再生に遅れる状態から、再生に余裕を持って先行して動作する状態に移行することを示しています。

ファーストパケットレイテンシ(図 9)は、コンカレンシー 64 において ~5884 ms (Batch) から ~632 ms に低下し、非同期チャンクが ~655 ms への最大の単一削減に貢献し、その後のレイヤーがその利得を維持しています。

デプロイ クイックスタート

デフォルトの omni プロファイルを使用して Qwen3-Omni-30B-A3B-Instruct をサービングするには:

vllm serve Qwen/Qwen3-Omni-30B-A3B-Instruct \
  --omni \
  --port 8091

明示的な設定については、ステージ化されたデプロイプロファイルを提供します:

vllm serve Qwen/Qwen3-Omni-30B-A3B-Instruct \
  --omni \
  --port 8091 \
  --deploy-config vllm_omni/deploy/qwen3_omni_moe.yaml

vLLM-Omni はプロファイルの platforms: セクションから一致するプラットフォームの差分を自動的にマージするため、起動コマンドは CUDA、NPU、ROCm、および XPU で変更なしで使用できます。

リクエストは /v1/chat/completions に送信する必要があります。リクエストボディの modalities フィールドを設定して出力タイプを宣言します:テキストのみの場合は ["text" ]、テキストと音声の場合は ["text", "audio" ] です。

非同期チャンク設定、マルチレプリカレイアウト、およびその他のデプロイオプションの詳細については、https://github.com/vllm-project/vllm-omni/blob/main/examples/online_serving/qwen3_omni/README.md にある Qwen3-Omni オンラインサービングガイドを参照してください。

謝辞

本投稿は、vLLM-Omni の Qwen3-Omni 貢献者である Haiyan Wu、Taichang Zhou、Canlin Guo、Ruirui Yang、Ziming Huang、Wengang Zheng、Lianhao Xu、Han Gao、Junhong Liu、Samit Huang、Hao Chen、Alex Brooks、Chenguang Zheng、Peiqi Yin、Wenjing Chen、Nick Cao、Shunyang Li、Yong Yang、Divyansh Singhvi、Yueqian Lin、Dayu Qiu、Roger Wang、および Hongsheng Liu の貢献とフィードバックに感謝します。

参考文献

  • Qwen3-Omni パイプライントポロジー: pipeline.py
  • Qwen3-Omni モデルラッパー: qwen3_omni.py
  • Qwen3-Omni ステージ入力プロセッサ: stage_input_processors/qwen3_omni.py
  • Qwen3-Omni デプロイプロファイル: qwen3_omni_moe.yaml
  • Qwen3-Omni 非同期チャンク パフォーマンス設定: test_qwen3_omni_async_chunk.json
  • Qwen3-Omni マルチレプリカ パフォーマンス設定: test_qwen3_omni_multi_replicas.json
  • Qwen3-Omni モデルリポジトリ: Qwen/Qwen3-Omni-30B-A3B-Instruct
  • 最適化プルリクエスト: CUDA Graph (Thinker #523, Talker #669, Code2Wav #2376); Async chunk (cross-stage #727, async scheduling #951, inter-packet latency #1656); Async output (#4476); Stage replicas (multi-stage #2396, runtime and control plane #3855); Hot-path cleanup (#3007, #3164, #3878)
  • Qwen3-Omni サービングまたは omni-modality 推論に興味がある場合は、vLLM Slack の #sig-omni チャンネルに参加するか、vLLM-Omni GitHub リポジトリで Issue を作成してください。

Sources