Bound Whisper VRAM across worker replicas, gte on CUDA, multi-worker GPU docs - #918
Open
hischampion wants to merge 2 commits into
Open
hischampion wants to merge 2 commits into
hischampion wants to merge 2 commits into
Conversation
…i-worker GPU tuning With several worker replicas on one GPU, every replica that reaches the Whisper fallback loads its own ~1.5 GB pipeline, so three concurrent transcriptions fill a 12 GB card and a fourth replica OOMs - even though CLAP/MusiCNN alone would leave room for many more. This adds an opt-in N-slot semaphore (flock files in a shared volume, LYRICS_ASR_LOCK_DIR / LYRICS_ASR_LOCK_SLOTS) around the ASR call: the slot is taken before the ASR alarm starts so queueing is never charged against the timeout, and the pipeline is unloaded before the slot is released so two resident copies never overlap. POSIX only; the Windows native build skips it. LYRICS_GTE_USE_GPU (default false) runs the gte lyrics-embedding model on CUDA, which on a shared GPU is cheaper than the CPU cores it frees for audio decoding. docker-compose-nvidia.yaml gains the asr-locks volume and commented examples for the env and a per-replica `cpus:` quota - without a cgroup limit cpu_budget.py sees the whole host and every replica opens ONNX pools as wide as the machine. docs/GPU.md gets a section with measurements: 3 replicas / 10 tracks/min at the VRAM ceiling -> 8 replicas / 38 tracks/min. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Author
|
This sped up my analysis 4x on my machine. I am not sure if we should have something set sane defaults to avoid thrashing or if we should leave it for a followup. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



AS-IS
Running several
audiomuse-ai-workerreplicas on one GPU stops scaling early, for three independent reasons:cpu_budget.pysees the whole host, so every replica opens ONNX thread pools as wide as the machine. Six replicas on a 20-thread host = 600+ threads, load average ~50, and the replicas mostly thrash each other.CPUExecutionProvider, so a transformer pass per lyric runs on the cores that audio decoding needs, while the GPU sits at 10-20%.TO-BE
All opt-in, defaults unchanged:
LYRICS_ASR_LOCK_DIR/LYRICS_ASR_LOCK_SLOTS— an N-slot semaphore made offlockfiles in a directory every replica mounts (newasr-locksvolume indocker-compose-nvidia.yaml). At most N replicas run Whisper concurrently; the rest wait. The slot is taken before the ASR alarm starts so queueing is never charged against the 300 s timeout, and the pipeline is unloaded before the slot is released so two resident copies never overlap. A crashed worker releases its slot via the kernel. POSIX only — the Windows native build skips it (guardedfcntlimport).LYRICS_GTE_USE_GPU—resolve_providers(label='gte', cpu_only_default=not LYRICS_GTE_USE_GPU). Off by default because the int8 graph picks up Memcpy nodes on CUDA; on a shared GPU it still wins.cpus:quota (whichcpu_budget.pyalready honors, it just is never set), and a "Several workers on one GPU" section indocs/GPU.mdwith the measurements below.Test
Measured on an RTX 5070 (12 GB), i7-12700K (20 threads), 32 GB RAM, Docker in WSL2,
latest-nvidiaimage v3.6.1, Navidrome 0.61.2, 5.7k-track library, ~25% of tracks reaching the Whisper fallback:cpus/ replicaFull library analyzed end to end with 8 replicas (5,724 tracks, no CUDA OOM, no worker restarts), then a lyrics-only re-run of 828 tracks. Whisper pass latency also dropped (median 20 s → 4-6 s) once the semaphore stopped 3-4 decoders contending for the GPU at the same time.
Semaphore unit check inside the image: 6 threads / 2 slots → peak concurrency 2; with
LYRICS_ASR_LOCK_DIRempty_acquire_asr_slot()returnsNoneand_release_asr_slot(None)is a no-op.To reproduce:
docker-compose-nvidia.yaml, uncommentLYRICS_ASR_LOCK_DIR/LYRICS_ASR_LOCK_SLOTS/cpus, dropcontainer_nameon the worker,docker compose up -d --scale audiomuse-ai-worker=8, watchnvidia-smianddocker stats.Other useful information
The same tuning applies to Kubernetes: a shared RWX volume for the lock dir and a CPU limit on the worker pod. Not tested there.
The gte-on-CUDA session logs the ONNX Runtime "N Memcpy nodes are added" warning — expected for this int8 graph, harmless.
Checklist
Type of change:
Tested on architecture:
Tested on media server:
Updated:
Other:
Not tested on ARM, the non-NVIDIA images, or Jellyfin/Emby/Lyrion — the change is media-server independent (lyrics stage only) and no-op unless the new env vars are set, but flagging it per CONTRIBUTING.md. The lyrics-stage code path is the same on ARM; the
fcntlguard covers the Windows native build.🤖 Generated with Claude Code