diff --git a/wsai/backends/melo_worker.py b/wsai/backends/melo_worker.py index b1ab879..16f75ce 100644 --- a/wsai/backends/melo_worker.py +++ b/wsai/backends/melo_worker.py @@ -67,8 +67,23 @@ def main() -> None: raise speaker_id = tts.hps.data.spk2id[lang] load_ms = int((time.monotonic() - t0) * 1000) - _emit({"ready": True, "ms": load_ms, "device": device}) - _log(f"[melo_worker] model ready in {load_ms} ms on {device}") + + # Warm up before signalling ready: the first CUDA synth pays a large lazy + # cost (kernel autotune/cudnn), ~10s cold vs ~130ms hot, which would blow the + # voice loop's ~1s budget on the very first reply. Do that dummy synth here so + # "ready" means "hot". Failures must not block startup. + warmup_ms = None + try: + warm_out = os.path.expanduser("~/.cache/wsai/tts/_warmup.wav") + os.makedirs(os.path.dirname(warm_out), exist_ok=True) + w = time.monotonic() + tts.tts_to_file("워밍업", speaker_id, warm_out, speed=1.3) + warmup_ms = int((time.monotonic() - w) * 1000) + except Exception as exc: + _log(f"[melo_worker] warmup skipped: {exc}") + + _emit({"ready": True, "ms": load_ms, "device": device, "warmup_ms": warmup_ms}) + _log(f"[melo_worker] model ready in {load_ms} ms on {device} (warmup {warmup_ms} ms)") for line in sys.stdin: line = line.strip() diff --git a/wsai/backends/whisper_worker.py b/wsai/backends/whisper_worker.py index df85c18..16ea832 100644 --- a/wsai/backends/whisper_worker.py +++ b/wsai/backends/whisper_worker.py @@ -76,8 +76,24 @@ def main() -> None: raise compute = _compute_for(device) load_ms = int((time.monotonic() - t0) * 1000) - _emit({"ready": True, "ms": load_ms, "device": device, "model": model_name}) - _log(f"[whisper_worker] {model_name} ready in {load_ms} ms on {device}/{compute}") + + # Warm up before signalling ready: the first CUDA transcribe pays a large + # lazy cost (kernel autotune), which would slow the first real utterance. + # Run a dummy transcribe on 1s of silence here so "ready" means "hot". + warmup_ms = None + try: + import numpy as np + + w = time.monotonic() + segs, _ = model.transcribe(np.zeros(16000, dtype=np.float32), language=default_lang) + for _ in segs: # segments are lazy; drain to force the actual compute + pass + warmup_ms = int((time.monotonic() - w) * 1000) + except Exception as exc: + _log(f"[whisper_worker] warmup skipped: {exc}") + + _emit({"ready": True, "ms": load_ms, "device": device, "model": model_name, "warmup_ms": warmup_ms}) + _log(f"[whisper_worker] {model_name} ready in {load_ms} ms on {device}/{compute} (warmup {warmup_ms} ms)") for line in sys.stdin: line = line.strip()