vLLM-Omni 为 Qwen3-Omni-30B-A3B-Instruct 推理服务的优化方案

vLLM-Omni 为 Qwen3-Omni-30B-A3B-Instruct 推理服务的优化方案

TL;DR

vLLM-Omni 通过 Thinker、Talker 和 Code2Wav 的分阶段流水线来提供 Qwen3-Omni-30B-A3B-Instruct 服务,并通过应用阶段级批处理(stage‑level batching)、CUDA Graph 捕获、异步分块交接(async chunk handoffs)、异步输出、Talker/Code2Wav 的阶段副本以及热路径清理,提升了在线推理性能,从而实现了更高的请求吞吐量、更低的音频首包延迟(TTFP)以及更低的实时因子(RTF)。

vLLM-Omni 中的 Qwen3-Omni 流水线

Qwen3-Omni 将多模态理解与语音生成相结合,在 vLLM-Omni 中以三阶段数据流的形式提供服务:Thinker 执行多模态推理和文本生成,Talker 将隐藏状态转换为 RVQ 编解码器代码,Code2Ww 从这些代码中重建波形音频。该流水线使用与 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 分离为独立的推理对象,允许每个阶段拥有自己的批处理、图(graph)和设备策略,而不是被迫共享单一策略(防止最慢的子路径阻塞其余部分)。逐阶段批处理将并发请求分组为一次 Talker MTP 调用和一次 Code2Wav 前向传播,填补了因单请求微任务而导致的 SM 空闲,并将固定的每步成本分摊到整个批次中。这种批处理、阶段分解的配置构成了后续所有优化的 Batch 基准。

CUDA Graph 捕获

CUDA Graph 通过一次捕获固定的算子序列并在执行时以极低的 CPU 开销进行重放,消除了每次解码步骤中重复的 CPU 端算子调度。Thinker 和 Talker 使用 vLLM 的外部 CUDA Graph 路径;Talker 的内部代码预测器通过 torch.compile 进行优化(不使用第二层图以避免冲突);Code2Wav 使用内部的 CUDAGraphDecoderWrapper,该包装器根据连接器配置中的 codec_chunk_framescodec_left_context_frames 捕获形状,预计算 SnakeBeta 缓存,并通过包装器的批处理或分块解码入口点分发分块。在所有三个阶段启用 CUDA Graph 后,在并发度为 64 时,请求吞吐量从 2.2 req/s 提升至 8.6 req/s (+299%),平均音频 TTFP 从 5884 ms 降至 2790 ms (-53%),平均音频 RTF 从 1.15 降至 0.59 (-49%)。

异步分块交接

异步分块(Async chunk)使用流水线式的部分交接取代了全负载阶段屏障。Thinker 增量地发出嵌入行;Talker 累积编解码帧并在 initial_codec_chunk_frames / codec_chunk_frames 边界处进行切片;异步调度器将分块传输与阶段计算重叠,允许前一个阶段仍在解码时,后一个阶段即可开始工作。这一改进带来了音频 TTFP 最大幅度的单项降幅,在并发度为 64 时,将平均音频 TTFP 从 2790 ms (CUDA Graph) 降至 655 ms (-77%),同时请求吞吐量从 8.6 req/s 小幅增加到 9.3 req/s (+8%);平均音频 RTF 保持在 0.63。

异步输出

异步输出通过将 Thinker 连接器负载的组装移动到非阻塞路径,实现了负载构建与阶段交接的解耦,因此解码工作器不会因嵌入和隐藏状态的同步复制而停顿。在已启用异步分块的基础上,异步输出在并发度为 64 时,使平均音频 TTFP 保持在 631 ms 左右(较异步分块降低 4%),同时将平均音频 RTF 从 0.63 降至 0.47 (-25%),并将请求吞吐量从 9.3 req/s 提高到 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 解码循环和连接器负载中随话语长度线性增长的每步开销。改进包括:在初始预填充后仅发送新的 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 通过异步输出固定每个 GPU 一个阶段(Thinker/Talker/Code2Wav 分别在 GPU 0/1/2,每个单副本);Stage 副本行在 GPU 0 上保留 Thinker,并在 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)在并发度 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

启动命令在 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。

Sources