diff --git a/.jules/bolt.md b/.jules/bolt.md index 96ecba4..efe25c0 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -1,3 +1,7 @@ ## 2024-02-18 - Pre-scaled Audio Feedback **Learning:** AudioFeedback volume scaling was applied during every playback, causing unnecessary numpy overhead and latency. **Action:** Pre-calculate scaled audio during `_load_and_cache` to minimize `_play_cached` latency (from ~1ms to ~0.04ms). + +## 2025-05-18 - Unlocked Garbage Collection +**Learning:** `ParakeetManager` held the model lock during `gc.collect()`, forcing concurrent `transcribe` requests (which require the lock) to wait for full GC completion before they could even *start* reloading the model. +**Action:** Release the lock before calling `gc.collect()`. This allows `transcribe` to acquire the lock and begin `load_model` (which releases GIL for C++ ops) in parallel with the garbage collection of the old model. diff --git a/src/chirp/parakeet_manager.py b/src/chirp/parakeet_manager.py index 54667ef..2cc1440 100644 --- a/src/chirp/parakeet_manager.py +++ b/src/chirp/parakeet_manager.py @@ -65,11 +65,15 @@ def _monitor_loop(self) -> None: self._unload_model() def _unload_model(self) -> None: + should_collect = False with self._lock: if self._model is not None and (time.time() - self._last_access > self._timeout): self._logger.info("Unloading Parakeet model to free memory.") self._model = None - gc.collect() + should_collect = True + + if should_collect: + gc.collect() def ensure_loaded(self): with self._lock: