项目文件夹

文件
wehub-resource-sync eec33d25b2
Build Wheel / build (3.11) (push) Failing after 1s
Build Wheel / build (3.12) (push) Failing after 0s
pre-commit / pre-commit (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:29:08 +08:00
..

TTS Universal Benchmark

A model-agnostic serving benchmark for TTS models in vllm-omni. One CLI (bench_tts.py) + one YAML registry (model_configs.yaml) drive perf and quality runs for every registered checkpoint: Qwen3-TTS (Base / CustomVoice) and VoxCPM2 today, more to come.

The same three task types — voice_clone, default_voice, voice_design — are wired into both the manual CLI and the DFX nightly CI matrix (tests/dfx/perf/tests/test_tts.json).

Quick start

1. Start the server

vllm serve Qwen/Qwen3-TTS-12Hz-1.7B-Base --omni --port 8000

The server auto-loads its Deploy YAML from vllm_omni/deploy/qwen3_tts.yaml (Pipeline + Deploy schema introduced in #2383). No --stage-configs-path or --deploy-config flag is needed for any registered model.

2. Run the benchmark (vllm bench serve --omni)

The primary, directly-controllable path. Copy-paste one of these and tweak any bench flag (sampling params, endpoint, extra body, warmups, etc.):

voice_clone (Qwen3-TTS-Base, seed-tts dataset)

vllm bench serve --omni \
    --host 127.0.0.1 --port 8000 \
    --model Qwen/Qwen3-TTS-12Hz-1.7B-Base \
    --backend openai-audio-speech \
    --endpoint /v1/audio/speech \
    --dataset-name seed-tts \
    --dataset-path /path/to/seed-tts-eval \
    --seed-tts-locale en \
    --num-prompts 20 --num-warmups 2 \
    --extra-body '{"task_type":"Base"}' \
    --max-concurrency 1 --request-rate inf \
    --percentile-metrics ttft,e2el,audio_rtf,audio_ttfp,audio_duration,audio_underrun \
    --save-result --result-dir ./results

default_voice (Qwen3-TTS-CustomVoice, bundled seed_tts_smoke)

vllm bench serve --omni \
    --host 127.0.0.1 --port 8000 \
    --model Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice \
    --backend openai-audio-speech \
    --endpoint /v1/audio/speech \
    --dataset-name seed-tts-text \
    --dataset-path benchmarks/build_dataset/seed_tts_smoke \
    --seed-tts-locale en \
    --num-prompts 20 --num-warmups 2 \
    --extra-body '{"voice":"Vivian","language":"English","task_type":"CustomVoice"}' \
    --max-concurrency 1 --request-rate inf \
    --percentile-metrics ttft,e2el,audio_rtf,audio_ttfp,audio_duration,audio_underrun \
    --save-result --result-dir ./results

voice_design (Qwen3-TTS-CustomVoice, bundled seed_tts_design)

vllm bench serve --omni \
    --host 127.0.0.1 --port 8000 \
    --model Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice \
    --backend openai-audio-speech \
    --endpoint /v1/audio/speech \
    --dataset-name seed-tts-design \
    --dataset-path benchmarks/build_dataset/seed_tts_design \
    --seed-tts-locale en \
    --num-prompts 20 --num-warmups 2 \
    --extra-body '{"task_type":"VoiceDesign","language":"English"}' \
    --max-concurrency 1 --request-rate inf \
    --percentile-metrics ttft,e2el,audio_rtf,audio_ttfp,audio_duration,audio_underrun \
    --save-result --result-dir ./results

Streaming continuity (audio_underrun)

audio_underrun (seconds) is the per-request worst-case buffer deficit under a realtime-rate player simulation. The audio-speech backend records chunk arrival times during the SSE stream and surfaces:

  • Mean / Median / P{50,99} AUDIO_UNDERRUN (s) per the standard percentile output - any positive value means at least one inter-chunk gap was longer than the chunk's audio duration.
  • Streaming continuity OK rate - fraction of requests whose worst-case underrun stayed under the threshold (default 100 ms, the commonly cited "audible gap" budget). Override the threshold with VLLM_OMNI_BENCH_AUDIO_CONTINUITY_THRESHOLD_S=<float>.

This captures the failure mode where RTF_p50 < 1 (server keeps up in aggregate) but per-stream chunk arrival is bursty enough that listeners still hear gaps - common at high concurrency on streaming TTS pipelines.

Add WER / SIM / UTMOS to any of the above

Append --seed-tts-wer-eval (and optionally SEED_TTS_EVAL_DEVICE=cuda:0 in the env, per PR #2558). This triggers the seed-tts-eval protocol: Whisper-large-v3 ASR → WER, WavLM embeddings → SIM, balacoon/utmos → UTMOS.

3. Convenience wrapper (bench_tts.py)

If you're running the canonical configuration for a registered model, bench_tts.py loads the right defaults from model_configs.yaml and emits the exact vllm bench serve --omni command above — useful for concurrency sweeps and multi-task runs:

# Smallest smoke — 5 prompts, concurrency=1
python benchmarks/tts/bench_tts.py \
    --model Qwen/Qwen3-TTS-12Hz-1.7B-Base \
    --task voice_clone \
    --dataset-path /path/to/seed-tts-eval \
    --concurrency 1 --num-prompts 5 \
    --output-dir ./results

# Full concurrency sweep
python benchmarks/tts/bench_tts.py \
    --model Qwen/Qwen3-TTS-12Hz-1.7B-Base \
    --task voice_clone \
    --dataset-path /path/to/seed-tts-eval \
    --concurrency 1 2 4 8 16 32 \
    --num-prompts 20 \
    --output-dir ./results

# With WER / SIM / UTMOS quality eval (adds ASR + embedding compute)
python benchmarks/tts/bench_tts.py \
    --model Qwen/Qwen3-TTS-12Hz-1.7B-Base \
    --task voice_clone \
    --dataset-path /path/to/seed-tts-eval \
    --wer-eval \
    --concurrency 4 --num-prompts 200 \
    --output-dir ./results

4. Plot a sweep

python benchmarks/tts/plot_results.py \
    --results ./results/*.json \
    --output ./results/curve.png

Outputs TTFP / RTF / throughput curves (and a markdown table) for every (task, concurrency) combination in the result set.

Task types

Task Dataset Request body Checkpoints that support it
voice_clone seed-tts ref_audio + ref_text + task_type=Base Qwen3-TTS-*-Base, VoxCPM2
default_voice seed-tts-text voice=Vivian + task_type=CustomVoice Qwen3-TTS-*-CustomVoice
voice_design seed-tts-design instructions=<natural-language description> + task_type=VoiceDesign Qwen3-TTS-*-CustomVoice

-CustomVoice checkpoints do NOT ship speaker_encoder weights, so voice_clone requests raise ValueError at model runtime. Use -Base for voice_clone.

Adding a new TTS model

Drop an entry into model_configs.yaml — no Python changes required:

models:
  <org>/<model-id>:
    supported_tasks: [voice_clone]          # or default_voice / voice_design
    backend: openai-audio-speech            # vllm bench serve backend
    endpoint: /v1/audio/speech              # OpenAI-compatible endpoint
    task_extra_body:                        # merged into every request's body
      voice_clone:
        task_type: Base

Then add the model's Deploy YAML under vllm_omni/deploy/<model>.yaml (Pipeline + Deploy schema) and it's immediately benchable.

Datasets

Dataset Bundled? Format Source
seed-tts-design 5-field meta.lst benchmarks/build_dataset/seed_tts_design/en/meta.lst (20 prompts)
seed_tts_smoke 4-field meta.lst benchmarks/build_dataset/seed_tts_smoke/en/meta.lst (20 text-only)
seed-tts 4-field meta.lst + WAVs Google-Drive: BytedanceSpeech/seed-tts-eval (~1.2 GB)
seed-tts-text 4-field meta.lst Same archive as seed-tts (wav column unused)

For manual voice_clone / default_voice runs against the full corpus, follow benchmarks/build_dataset/download_process_data_seedtts.md and point --dataset-path at the extracted seedtts_testset directory.

DFX nightly CI

tests/dfx/perf/tests/test_tts.json wires three perf regimes plus quality:

eval_phase concurrency purpose Baseline metrics
latency 1 Single-request TTFP / RTF SLO median_audio_ttfp_ms, median_audio_rtf
throughput 8 Codec-batching cliff sentinel (PDF #272 concurrency≥8) median_audio_ttfp_ms, median_audio_rtf
quality 4 WER / SIM / UTMOS regression (disabled in CI by default) mean_audio_rtf

Why median_* for latency/throughput and mean_* for quality: latency distributions have cold-start tails that drag the mean; quality aggregates over 200 prompts so single-request outliers don't matter.

Quality entries are enabled: false in CI because seed-tts-eval is not staged in the Buildkite container (matches the precedent in PR #2558 — quality runs are manual / release-validation, not nightly).

Concurrency cliff regression sentinel

Observed on H20-3e, Qwen3-TTS-1.7B (measured pre-merge on this branch):

Task Model c=1 c=4 c=8 c=16 c=32
voice_clone 1.7B-Base RTF 0.15 / TTFP 165ms 0.28 / 412ms 0.49 / 1701ms 0.72 / 3355ms 0.77 / 3772ms
voice_design 1.7B-CustomVoice RTF 0.08 / TTFP 53ms 0.11 / 154ms 0.21 / 872ms 0.33 / 1801ms 0.38 / 1989ms

Both models show a 4–6× TTFP jump from c=4 to c=8 while audio throughput saturates around c=4–8 — the codec-bs=1 bottleneck documented in vllm-project/vllm-omni#272. The throughput CI regime at c=8 is the sentinel for regressions in this area.

File layout

benchmarks/tts/
├── README.md                  (this file)
├── bench_tts.py               CLI — serve-mode benchmark driver
├── plot_results.py            Generate per-task / per-concurrency curves
└── model_configs.yaml         Model registry (supported tasks + extra body)