Qwen3-Omni-30B-A3B-Instruct 서빙을 위한 vLLM-Omni 최적화

Qwen3-Omni-30B-A3B-Instruct 서빙을 위한 vLLM-Omni 최적화

TL;DR

vLLM-Omni는 Thinker, Talker, Code2Wav로 구성된 단계별 파이프라인을 통해 Qwen3-Omni-30B-A3B-Instruct를 서빙하며, stage‑level batching, CUDA Graph capture, async chunk handoffs, async output, Talker/Code2Wav를 위한 stage replicas 및 hot‑path cleanup을 적용하여 온라인 서빙 성능을 향상시켰습니다. 그 결과 요청 처리량(throughput)이 높아지고, 오디오 TTFP(time‑to‑first‑packet)와 실시간 계수(real‑time factor)가 낮아졌습니다.

vLLM-Omni의 Qwen3-Omni 파이프라인

Qwen3-Omni는 멀티모달 이해와 음성 생성을 결합하며, vLLM-Omni에서는 세 단계의 데이터 흐름으로 서빙됩니다. Thinker는 멀티모달 추론과 텍스트 생성을 수행하고, Talker는 hidden states를 RVQ codec codes로 변환하며, Code2Wav는 해당 코드로부터 waveform 오디오를 재구성합니다. 이 파이프라인은 OpenAI 호환 /v1/chat/completions 엔드포인트를 사용하며, 요청 본문의 modalities 필드에서 ["text" ] 또는 ["text", "audio" ]와 같이 출력 유형을 지정할 수 있습니다. 기본 배포 프로필은 --omni로 실행할 때 자동으로 선택됩니다. --deploy-config vllm_omni/deploy/qwen3_omni_moe.yaml를 통해 명시적인 프로필을 제공할 수도 있습니다. vLLM-Omni는 프로필의 platform 섹션에서 플랫폼별 차이점(deltas)을 병합하므로, 동일한 실행 명령어로 CUDA, NPU, ROCm, XPU 백엔드 모두에서 작동합니다.

최적화 기술

단계 분해 및 배칭 (Stage Decomposition and Batching)

단계 분해는 Thinker, Talker, Code2Wav를 독립적인 서빙 객체로 분리하여, 각 단계가 가장 느린 하위 경로에 의해 전체가 제한되는 단일 정책을 공유하는 대신 각자 고유한 batching, graph 및 device 정책을 가질 수 있도록 합니다. 단계별 배칭은 동시 요청들을 하나의 Talker MTP 호출과 하나의 Code2Ww forward로 그룹화하여, 단일 요청의 마이크로 작업으로 인해 유휴 상태가 될 수 있는 SM을 채우고 고정된 단계별 비용을 배치 전체에 분산시킵니다. 이러한 배치된 단계 분해 구성은 이후 모든 최적화의 기준점(Batch baseline)이 됩니다.

CUDA Graph Capture

CUDA Graph는 고정된 연산 시퀀스를 한 번 캡처한 후 최소한의 CPU 작업으로 재실행함으로써, 매 디코드 단계마다 반복되는 CPU 측 커널 디스패치를 제거합니다. Thinker와 Talker는 vLLM의 외부 CUDA Graph 경로를 사용합니다. Talker의 내부 코드 예측기는 torch.compile로 최적화되었습니다(충돌을 피하기 위해 두 번째 graph 레이어는 사용하지 않음). Code2Wav는 커넥터 설정의 codec_chunk_framescodec_left_context_frames를 기반으로 형상을 캡처하고, SnakeBeta 캐시를 사전 계산하며, wrapper의 배치 또는 청크 디코드 엔트리 포인트를 통해 청크를 디스패치하는 내부 CUDAGraphDecoderWrapper를 사용합니다. 세 단계 모두에서 CUDA Graph를 활성화하면 요청 처리량이 2.2에서 8.6 req/s (+299%)로 증가하고, 평균 오디오 TTFP는 5884 ms에서 2790 ms(−53%)로 단축되며, 동시성 64에서 평균 오디오 RTF는 1.15에서 0.59(−49%)로 감소합니다.

Async Chunk Handoffs

Async chunk는 전체 페이로드 단계 장벽(barriers)을 파이프라인 방식의 부분 전달(partial handoffs)로 대체합니다. Thinker는 임베딩 행을 점진적으로 방출하고, Talker는 codec 프레임을 축적하여 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을 유지했습니다.

Async Output

Async output은 Thinker 커넥터 페이로드의 조립을 비차단(non-blocking) 경로로 이동시켜 페이로드 구축을 단계 간 전달과 분리함으로써, 디코드 워커가 임베딩 및 hidden states의 동기식 복사로 인해 중단되지 않도록 합니다. Async chunk가 이미 활성화된 상태에서, async output은 평균 오디오 TTFP를 약 631ms(−4% from async chunk)로 유지하면서, 동시성 64에서 평균 오디오 RTF를 0.63에서 0.47(−25%)으로 낮추고 요청 처리량을 9.3에서 11.3 req/s(+22%)로 증가시킵니다.

Stage Replicas

Stage replicas는 부하가 걸리는 단계에만 용량을 추가합니다. 각 요청은 수백 개의 Talker 디코드 단계와 Code2Wav vocoder forward를 필요로 하는 반면, Thinker 생성은 단 한 번만 수행되므로 Talker와 Code2Wav가 먼저 병목 현상이 발생합니다. GPU 0에 단일 Thinker를 유지하면서 GPU 1과 2에 2× Talker 및 2× Code2Wav 복제본을 배포하면, 대규모 멀티모달 Thinker를 중복 생성하지 않고도 음성 측의 백로그를 흡수할 수 있습니다. Async output 위에 복제본을 추가하면 동시성 64에서 요청 처리량이 11.7 req/s(+4% from async output)까지 상승하며, 평균 오디오 TTFP는 약 632 ms, 평균 오디오 RTF는 0.47을 유지합니다.

Hot-Path Cleanup

Hot-path cleanup은 Talker 디코드 루프와 커넥터 페이로드에서 발화 길이에 따라 증가하는 단계별 오버헤드를 제거합니다. 변경 사항은 다음과 같습니다: 초기 prefill 이후에는 새로운 embed.decode 행만 전송(단계별 커넥터 트래픽 O(1)), 멀티프로세스 오버헤드를 피하기 위해 기본적으로 단일 GPU uni executor 사용, 페이로드 구축 시 반복적인 torch.cat 제거, SDPA, 네이티브 GQA, 인라인 top-k 샘플링, 캐시된 모듈 참조 및 torch.compile(충돌하는 두 번째 graph 레이어 없음)을 사용하여 re-prefill을 사용하도록 Talker 코드 예측기 재작성, 중간 텐서를 model_intermediate_buffer에 GPU 상주 상태로 유지, 불필요한 device-to-host 읽기 건너뛰기, 불필요한 멀티모달 위치 계산 방지. 긴 컨텍스트 단일 요청 테스트에서 이러한 변경을 통해 엔드 투 엔드 지연 시간이 21.28 s에서 7.37 s로, 오디오 TTFP는 3197 ms에서 1796 ms로, 오디오 RTF는 0.71에서 0.28로 감소했습니다. 이러한 이점은 위의 최적화들과 결합되어 DFX perf suite 베이스라인에 반영됩니다.

검증 결과

검증에는 모델 Qwen3-Omni-30B-A3B-Instruct, 프롬프트 길이 10/160/320/640 토큰, 동시성 수준 1/16/32/64, 5회의 warmup, 0/1/2로 매핑된 3개의 가시적 GPU를 사용하여 Seed-TTS en에 대한 통제된 벤치마크 스윕을 사용했습니다. 각 구성은 격리된 배포 프로필로 서버를 재시작하고 이전 행에 최적화를 하나씩 추가했습니다. Batch부터 Async output까지는 GPU당 한 단계(GPU 0/1/2에 각각 Thinker/Talker/Code2Wav, 단일 복제본)를 고정했습니다. Stage replicas 행은 GPU 0에 Thinker를 유지하고 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

처리량(Figure 7)을 보면 동시성 64에서 요청 처리량이 Batch baseline인 2.2 req/s에서 11.7 req/s(5.4×)로 증가하고, 동시성 32에서는 1.1에서 6.8 req/s로 증가함을 보여줍니다. 가장 큰 단일 도약은 CUDA Graph(4×)입니다. async output는 높은 동시성에서 마지막 추진력을 제공하며, stage replicas는 동시성이 증가함에 따라 여유 공간이 커지는 피크 처리량을 제공합니다.

실시간 계수(Figure 8)는 Batch 환경의 실시간 미만(above-real-time) 1.15에서 동시성 64에서 0.47로 떨어지며, 이는 부하가 걸릴 때 디코드가 재생보다 뒤처지는 상태에서 재생보다 훨씬 앞서 나가는 상태로 이동함을 나타냅니다.

첫 번째 패킷 지연 시간(Figure 9)은 동시성 64에서 ~5884 ms(Batch)에서 ~632 ms로 떨어지며, async chunk가 ~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" ]입니다.

async-chunk 설정, 다중 복제본 레이아웃 및 추가 배포 옵션에 대한 자세한 내용은 https://github.com/vllm-project/vllm-omni/blob/main/examples/online_serving/qwen3_omni/README.md 에 있는 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의 기여와 피드백에 감사하며 vLLM‑Omni의 Qwen3‑Omni 기여자들에게 감사를 표합니다.

참고 문헌

  • 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 async‑chunk 성능 설정: 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 서빙 또는 omni‑modality 추론에 관심이 있다면 vLLM Slack의 #sig-omni 채널에 참여하거나 vLLM‑Omni GitHub 저장소에 이슈를 생성하십시오.

Sources