vllm-project/vllm-omni

[RFC]: Qwen3-TTS high-throughput hardening

Open

#3,535 opened on May 12, 2026

View on GitHub
 (2 comments) (5 reactions) (1 assignee)Python (1,067 forks)github user discovery
help wantedhigh prioritytts

Repository metrics

Stars
 (4,990 stars)
PR merge metrics
 (PR metrics pending)

Description

Motivation.

Streaming Qwen3-TTS at c ≥ 64 currently produces audible audio gaps for every user. Our primary business workload is Qwen3-TTS-Base + voice_clone, streamed via OpenAI /v1/audio/speech. SLO target is max_underrun_ms_p95 ≤ 100 ms; every cell currently misses this by 50–200×.

Bench setup: H20, bench_tts_client_continuity.py per-chunk underrun tracking, default vs 2-GPU split (s0=128, s1=1, devices: 0/1).

Insights (data behind the workstreams):

insight data implication
Continuity fails by 50–200× SLO across every cell Base voice_clone undr_p99: 16.2 / 17.7 / 19.1 s at c=64/128/256. CV: 8.6 / 8.6 / 8.4 s. SLO is 100 ms Throughput fixes alone cannot meet SLO; scheduler RFC needed in parallel
2-GPU split helps voice_clone, hurts CV at c ≥ 128 Base c=128 undr_p99: 17.7 → 16.2 s (better). CV c=128: 8.6 → 11.6 s (worse). CV c=256: 8.4 → 12.0 s (worse) Single-axis "split stages" is not enough; Stage 1 becomes new bottleneck for CV at high c
voice_clone's Stage 0 is 11× slower than CV's chunk-emit interval (per @ischencheng profile): voice_clone 263 ms, CV 23 ms voice_clone is Stage-0-bound; CV is Stage-1-bound. Optimizations are not symmetric
Stage 1 default max_num_seqs: 1→10 already gives 2–3× TTFA at c=4/8 PR #3322 ablation by @ischencheng yaml-level batching works; structural batched forward (chunked_decode with bs>1) contributes ~0% — confirmed
enable_prefix_caching=true crashes Qwen3-Omni serving Issue #3510 reproducer Ref-audio KV reuse via vLLM prefix cache is blocked until #3510 is fixed
Stage 1 scheduler is per-chunk round-robin Codex review of omni_generation_scheduler.py:71-72 + chunk_transfer_adapter.py:407-428 128 streams interleave through Stage 1 chunk-by-chunk; out-of-scope for this RFC, scheduler RFC will address

Scope: high-throughput (c ≥ 32) deployment, voice_clone primary. Out of scope: scheduler / SLA work, low-latency mode, MIG/MPS, async D2H pipeline (#3509) — each gets its own RFC.

Proposed Change.

Five parallel workstreams. SLO-level continuity (100 ms underrun) requires a separate scheduler RFC.

Tasks

WS task owner scope acceptance justification
WS-1 AR / talker optimization @Sy0307 Land 1–2 PRs porting #3221's talker patterns (graph-safe sampling primitives, @support_torch_compile evaluation, kernel tuning) into existing vllm_omni/.../qwen3_tts/ talker. No new model arch Base voice_clone Stage 0 TTFT or chunk-emit interval improves ≥ 15% at c=4/8 voice_clone Stage-0-bound (263 ms vs 23 ms); #3221 has working thinker patterns
WS-2 Prefix / speaker cache for ref-audio KV reuse @JuanPZuluaga Primary: debug #3510, get enable_prefix_caching=true working on omni path. Fallback: extend SpeakerCacheManager (#2630) to be KV-aware (get_kv/put_kv/invalidate, LRU eviction) At ref_audio_hash reuse ≥ 80%, Base voice_clone TTFT_p50 at c=8–32 improves ≥ 30% voice_clone is Stage-0-bound; ref-audio prefill is the heavy step; reuse eliminates it on cache hit
WS-3 Prefilling optimization (cache-miss path) @R2-Y Pick 1–2: cross-request batched ref-audio prefill / chunked prefill for ref-audio segment / FP8 ref-audio encoder Cache-miss Base voice_clone TTFT improves ≥ 20% over main voice_clone Stage-0-bound; WS-2 covers cache-hit, WS-3 covers cache-miss
WS-4 Adaptive producer-consumer (credit-based) @linyueqian Per-stream chunk credits between Stage 0 and Stage 1. Stage 0 scheduler skips credit=0 streams in LM step. Static credit count first; adaptive is follow-up SHM queue depth bounded; Stage 0 idle on backpressured streams decreases vs baseline At c=128 with 1 Stage 1 replica, producer/consumer rates are mismatched; current adapter queue is unbounded
WS-5 Code2wav batch + multi-replica @ischencheng Build on PR #3322. Add multi-replica Stage 1 via num_replicas (framework merged). Verify sticky per-stream routing in SHM connector. Publish (num_replicas, s1) config table At c=128/256, 2× Stage 1 replicas improve RTF ≥ 30% over 1-replica, no routing bugs CV becomes Stage-1-bound at c ≥ 128 under 2-GPU split (insight #2); horizontal Stage 1 scaling is the only path

Dependencies

WS-1 (talker)        — independent
WS-5 (multi-replica) — independent
WS-2 (cache)         — independent (primary path = #3510 debug; fallback = self-managed)
   └─→ WS-3 (cache-miss path)
                                ┌─ WS-5 throughput data
WS-4 (adaptive)        ←────────┤
                                └─ WS-2 cache hit/miss state

WS-1 / WS-2 / WS-5 startable immediately. WS-3 / WS-4 have soft dependencies (can default while starting).

Aggregate acceptance

metric Base voice_clone c=128 CustomVoice c=128
max_underrun_ms_p95 substantially improved (< 2 s; 100 ms SLO needs scheduler RFC) same
TTFT_p99 / TTFT_p50 ≤ 3.0 ≤ 3.0
RTF_p50 not worse than current best by > 5% same
success_rate 100% 100%
consolidated deliverable vllm_omni/deploy/qwen3_tts_high_throughput.yaml published with WS-5 recommended config

This RFC does not commit to meeting the 100 ms underrun SLO. Scheduler RFC handles that.

Out of scope

topic reason future RFC
Scheduler / SLA (Stage 1 bounded-K active-stream window, admission control) Required for 100 ms SLO but is structural change to OmniGenerationScheduler yes
Low-latency mode (c ≤ 4) Different operating point; NVIDIA #3221 single-stage + TensorRT is the natural fit yes
MIG / MPS GPU partitioning Infrastructure-side, separate concern yes
Async D2H + stage pipeline transfer @tzhouam owns via #3509 cross-ref only

Risks

R1 WS-5 multi-replica may surface SHM connector cross-replica routing bugs. Sticky per-stream routing must be verified before claiming throughput win
R2 WS-2 primary path depends on #3510 being tractable. If structural, fallback (self-managed SpeakerCacheManager extension) adds engineering cost without changing user-visible behavior
R3 WS-4 credit-based flow control may oscillate under variable load. Static credit first; adaptive is follow-up
R4 Scheduler is out of scope. voice_clone at c ≥ 128 will still produce audible gaps after this RFC. Manage expectations explicitly

Related in-flight work

ref who relation
PR #3485 merged initial_codec_chunk_frames=1 — first-chunk TTFP fix, already in main
PR #3322 / Issue #3163 @ischencheng Single-GPU code2wav batched decoding. WS-5 extends to multi-replica
PR #3221 @vklimkov-nvidia Single-stage talker + TensorRT vocoder. WS-1 borrows thinker patterns; does not merge
Issue #3510 @amy-why-3459 enable_prefix_caching=true causes Qwen3-Omni requests to hang. WS-2 addresses this
RFC #3509 @tzhouam Async D2H + stage pipeline transfer. Not pursued in this RFC; cross-referenced

(Bench charts and per-chunk underrun visualizations will be attached as images below.)

Feedback Period.

One week (until 2026-05-19). Each WS owner please confirm the assignment or propose adjustments by then.

CC List.

@Sy0307 @JuanPZuluaga @R2-Y @ischencheng @tzhouam @gcanlin @hsliuustc0106

Contributor guide