mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
perf(tts): reduce Kokoro time-to-first-audio with session warmup and threading (#342)
This commit is contained in:
@@ -64,7 +64,7 @@ const overrides = new Map([
|
||||
["src-tauri/src/huddle/stt.rs", 580], // STT pipeline + PTT edge-detection flush + PTT gating (is_speech AND ptt_active) + barge-in for VAD mode + rubato resampler + earshot VAD + sherpa-onnx transcription
|
||||
["src-tauri/src/huddle/preprocessing.rs", 670], // TTS text preprocessing pipeline + unified split_sentences + int_to_words 0-999999 + URL trailing punctuation preservation + 23 unit tests
|
||||
["src-tauri/src/huddle/relay_api.rs", 510], // audio relay recv task + per-peer frame counting for remote human TTS interrupt
|
||||
["src-tauri/src/huddle/tts.rs", 1010], // TTS pipeline + cancel/shutdown handling + apply_fades + 18 unit tests for remote interrupt mechanism
|
||||
["src-tauri/src/huddle/tts.rs", 1030], // TTS pipeline + session warmup + cancel/shutdown handling + apply_fades + 18 unit tests for remote interrupt mechanism
|
||||
]);
|
||||
|
||||
async function walkFiles(directory) {
|
||||
|
||||
@@ -590,6 +590,21 @@ pub fn load_text_to_speech(model_dir: &str) -> Result<KokoroTTS, String> {
|
||||
.find(|p| p.exists())
|
||||
.ok_or_else(|| format!("no model.onnx found in {model_dir}"))?;
|
||||
|
||||
// ── Session threading options ────────────────────────────────────────
|
||||
//
|
||||
// parallel_execution — runs independent graph operators concurrently.
|
||||
// Kokoro's graph is mostly sequential, so the benefit is modest, but
|
||||
// it's safe and free to enable.
|
||||
// intra_threads(0) — lets ONNX Runtime use all available CPU cores for
|
||||
// parallelism within individual operators (e.g., large matmuls).
|
||||
//
|
||||
// NOTE: GraphOptimizationLevel::All is already the ort default — no
|
||||
// need to set it explicitly. Accuracy-altering flags (approximate_gelu,
|
||||
// flush_to_zero) are intentionally omitted — they need A/B audio
|
||||
// validation before enabling for a TTS model. memory_pattern is omitted
|
||||
// because Kokoro input lengths vary per sentence and the ORT docs warn
|
||||
// against it for variable-size inputs.
|
||||
|
||||
// Try CoreML first (zero binary cost — macOS system framework).
|
||||
// If the model has ops CoreML can't handle (common with quantized models),
|
||||
// the EP registers fine but commit_from_file fails. Catch that and retry
|
||||
@@ -597,6 +612,10 @@ pub fn load_text_to_speech(model_dir: &str) -> Result<KokoroTTS, String> {
|
||||
let session = {
|
||||
let mut builder_with_coreml = Session::builder()
|
||||
.map_err(|e| format!("session builder: {e}"))?
|
||||
.with_parallel_execution(true)
|
||||
.map_err(|e| format!("parallel execution: {e}"))?
|
||||
.with_intra_threads(0)
|
||||
.map_err(|e| format!("intra threads: {e}"))?
|
||||
.with_execution_providers([ort::ep::CoreML::default()
|
||||
.with_compute_units(ort::ep::coreml::ComputeUnits::All)
|
||||
.with_model_format(ort::ep::coreml::ModelFormat::MLProgram)
|
||||
@@ -617,6 +636,10 @@ pub fn load_text_to_speech(model_dir: &str) -> Result<KokoroTTS, String> {
|
||||
// Retry without any execution providers — pure CPU.
|
||||
Session::builder()
|
||||
.map_err(|e| format!("session builder (CPU fallback): {e}"))?
|
||||
.with_parallel_execution(true)
|
||||
.map_err(|e| format!("parallel execution (CPU): {e}"))?
|
||||
.with_intra_threads(0)
|
||||
.map_err(|e| format!("intra threads (CPU): {e}"))?
|
||||
.commit_from_file(&model_path)
|
||||
.map_err(|e| format!("load model {} (CPU): {e}", model_path.display()))?
|
||||
}
|
||||
|
||||
@@ -232,6 +232,25 @@ fn tts_worker(
|
||||
}
|
||||
};
|
||||
|
||||
// ── 2b. Warmup inference ─────────────────────────────────────────────────
|
||||
// The first ONNX inference on any session is significantly slower than
|
||||
// subsequent ones — it triggers JIT compilation, memory pool allocation,
|
||||
// and (on CoreML) lazy model compilation. Run a short dummy synthesis and
|
||||
// discard the output so the first real utterance runs at warm-session speed.
|
||||
{
|
||||
let t = std::time::Instant::now();
|
||||
match engine.synth_chunk("warmup", "en", &style, SYNTH_STEPS, SYNTH_SPEED) {
|
||||
Ok(_) => eprintln!(
|
||||
"sprout-desktop: TTS warmup completed in {:.0}ms",
|
||||
t.elapsed().as_millis()
|
||||
),
|
||||
Err(e) => eprintln!(
|
||||
"sprout-desktop: TTS warmup failed after {:.0}ms: {e} — first utterance may be slow",
|
||||
t.elapsed().as_millis()
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// ── 3. Initialise rodio output device ─────────────────────────────────────
|
||||
use rodio::Player;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user