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 的階段式流水線進行服務,並透過應用階段級批處理 (stage‑level batching)、CUDA Graph 擷取、非同步分塊傳遞 (async chunk handoffs)、非同步輸出 (async output)、Talker/Code2Wav 的階段副本 (stage replicas) 以及熱路徑清理 (hot‑path cleanup),提升了線上服務效能,實現更高的請求吞吐量、更低的音訊首包延遲 (audio time‑to‑first‑packet) 以及更低的實時因子 (real‑time factor)。
vLLM-Omni 中的 Qwen3-Omni 流水線
Qwen3-Omni 將多模態理解與語音生成相結合,在 vLLM-Omni 中以三階段數據流的形式提供服務:Thinker 執行多模態推理與文本生成,Talker 將隱藏狀態 (hidden states) 轉換為 RVQ 編解碼碼,而 Code2Wav 則從這些碼中重建波形音訊。該流水線使用與 OpenAI 相容的 /v1/chat/completions 端點,請求主體中的 modalities 欄位用於指定輸出類型,例如 ["text" ] 或 ["text", "audio" ]。使用 --omni 啟動時會自動選擇預設的部署配置;也可以透過 --deploy-config vllm_omni/deploy/qwen3_omni_moe.yaml 提供明確的配置。由於 vLLM-Omni 會合併配置檔中 platform 區段的特定運行時差異,因此相同的啟動指令可適用於 CUDA、NPU、ROCm 與 XPU 後端。
優化技術
階段分解與批處理
階段分解將 Thinker、Talker 與 Code2Wav 分離為獨立的服務對象,允許每個階段擁有自己的批處理、圖形與設備策略,而不是被迫共享單一策略,以免最慢的子路徑限制了其餘部分。逐階段批處理將並發請求分組為一次 Talker MTP 調用與一次 Code2Wav 前向傳播,填補了因單一請求微型工作而導致的 SM 空閒,並將每步的固定成本分攤到整個批次中。這種批處理、階段分解的配置構成了所有後續優化的 Batch 基準線。
CUDA Graph 擷取
CUDA Graph 透過一次擷取固定的算子序列並以極低的 CPU 工作量進行重放,消除了每次解碼步驟中重複的 CPU 端內核調度。Thinker 與 Talker 使用 vLLM 的外部 CUDA Graph 路徑;Talker 的內部代碼預測器使用 torch.compile 進行優化(不使用第二層圖形以避免衝突);Code2Wav 使用內部的 CUDAGraphDecoderWrapper,根據連接器配置中的 codec_chunk_frames 與 codec_left_context_frames 擷取形狀,預先計算 SnakeBeta 快取,並透過 wrapper 的批處理或分塊解碼入口點傳送分塊。在所有三個階段啟用 CUDA Graph 可將請求吞吐量從 2.2 提升至 8.6 req/s (+299%),在並發度為 64 時,將平均音訊 TTFP 從 5884 ms 降低至 2790 ms (−53%),並將平均音訊 RTF 從 1.15 降低至 0.59 (−49%)。
非同步分塊傳遞
非同步分塊 (Async chunk) 以流水線式的部分傳遞取代了完整的負載階段屏障。Thinker 增量地發送嵌入行 (embedding rows);Talker 累積編解碼幀並在 initial_codec_chunk_frames / codec_chunk_frames 邊界進行切片;非同步調度器將分塊傳輸與階段計算重疊,允許每個階段在前一個階段仍在解碼時就開始工作。這項改變帶來了音訊 TTFP 最大的單次降幅,在並發度為 64 時,將平均音訊 TTFP 從 2790 ms (CUDA Graph) 降至 655 ms (−77%),同時請求吞吐量從 8.6 略微增加至 9.3 req/s (+8%);平均音訊 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 生成,因此 Talker 與 Code2Wav 會最先成為瓶頸。在 GPU 1 與 2 上部署 2× Talker 與 2× Code2Wav 副本,同時在 GPU 0 上保留單個 Thinker,可以在不複製大型多模態 Thinker 的情況下吸收語音端的積壓。在非同步輸出之上添加副本,可在並發度 64 時將請求吞吐量提升至 11.7 req/s (+4%),同時平均音訊 TTFP 保持在約 632 ms,平均音訊 RTF 為 0.47。
熱路徑清理
熱路徑清理移除了 Talker 解碼迴圈與連接器負載中隨語句長度增加而增加的每步開銷。更改包括:在初始預填充 (prefill) 後僅發送新的 embed.decode 行(每步連接器流量為 O(1))、預設使用單 GPU uni 執行器以避免多進程開銷、消除負載構建中重複的 torch.cat、重寫 Talker 代碼預測器以使用帶有 SDPA、原生 GQA、內聯 top-k 採樣、快取模組引用與 torch.compile(無衝突的第二層圖形)、將中間張量保留在 GPU 中的 model_intermediate_buffer、跳過冗餘的設備到主機讀取,並避免不必要的多模態位置計算。在一個長上下文單請求測試中,這些更改將端到端延遲從 21.28 s 降低至 7.37 s,音訊 TTFP 從 3197 ms 降低至 1796 ms,音訊 RTF 從 0.71 降低至 0.28。這些增益與上述優化疊加,並反映在 DFX 效能套件的基準線中。
驗證結果
驗證是在 Seed-TTS en 上使用模型 Qwen3-Omni-30B-A3B-Instruct 進行受控基準測試掃描,提示長度為 10/160/320/640 tokens,並發層級為 1/16/32/64,進行了五次熱身,並將三個可見 GPU 映射為 0/1/2。每個配置都使用隔離的部署配置重啟伺服器,並在前一行基礎上增加一個優化。Batch 透過 Async output 在每個 GPU 上固定一個階段(Thinker/Talker/Code2Wav 分別位於 GPU 0/1/2,各單個副本);Stage replicas 行則將 Thinker 保留在 GPU 0,並在 GPU 1 與 2 上運行 2× Talker + 2× Code2Wav。
在並發度 64 時:
- Batch 基準線: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 基準線的 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)從 ~5884 ms (Batch) 在並發度 64 時降至 ~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
啟動指令在 CUDA、NPU、ROCm 與 XPU 上均可直接使用,因為 vLLM-Omni 會自動從配置檔的 platforms: 區段合併匹配的平台差異。
請求應發送到 /v1/chat/completions。在請求主體中設置 modalities 欄位以宣告輸出類型:["text" ] 代表僅文本,或 ["text", "audio" ] 代表文本加語音。
有關非同步分塊設置、多副本佈局及進一步部署選項的詳情,請參閱 Qwen3-Omni 線上服務指南:https://github.com/vllm-project/vllm-omni/blob/main/examples/online_serving/qwen3_omni/README.md。
致謝
本文感謝 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 - 優化 Pull Requests: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 服務或全模態推理感興趣,請加入 vLLM Slack 中的
#sig-omni頻道,或在 vLLM-Omni GitHub 儲存庫中提出 Issue。