Skip to content

DGX inference services — isolated load/perf baseline plan

Follow-up to INCIDENT-2026-08-04 (#1397). The OOM happened because we ran every model service together under a batch burst with no idea of each one's memory-vs-load curve. This plan characterises each DGX service in isolation so we know its concurrency ceiling, memory-per-request, and /dev/shm behaviour before co-residing them again.

Blocked on: DGX power-cycle (box currently OOM-locked). Prepare now, run when it's back.

The box (constraint)

DGX Spark GB10, 130.7 GB unified CPU+GPU memory. "GPU memory" is system RAM — a model's reservation and a /dev/shm burst draw from the same pool. That's why per-service memory ceilings matter more here than on a discrete-GPU box.

Services under test (isolate one at a time)

Service Server (infra/dgx/) Endpoint Workload
Diarization pyannote-server :8001 POST /v1/diarize (multipart audio) real episode wav (short + long)
Whisper Turbo whisper-server / speaches :8000 POST /v1/audio/transcriptions (multipart) real episode wav (short + long)
vLLM (Qwen) vLLM :8003 POST /v1/completions (OpenAI-style) representative prompt + max_tokens
(secondary) MOSS moss-server :? POST (http) TBD
(secondary) ollama ollama :11434 TBD

Isolation setup: load only the target service (stop the others so the unified pool isn't pre-reserved) — otherwise the baseline is contaminated by the ~100 GB of other reservations. Record what else is resident when you run.

Method

For each service, ramp concurrency (1 → 2 → 4 → 8 …) with a fixed per-request workload, sustaining each level for ~60 s, and stop when the box approaches the OOM cliff. At every level capture:

Client-side (from the harness): p50/p95 latency, throughput (req/s; for audio: audio-minutes processed/s; for vLLM: tokens/s), error rate, first-failure concurrency.

Server-side (from homelab VictoriaMetrics, correlated to the run window): node_memory_MemAvailable_bytes (minimum), node_memory_Shmem_bytes (peak + delta — the /dev/shm signal that caused the incident), GPU util/mem where exposed.

The harness — scripts/perf/dgx_service_loadtest.py — does all of this and prints a per-level table, plus a ceiling guard that halts the ramp when MemAvailable dips below 10 GB (so a baseline run can't repeat the incident). Example:

python scripts/perf/dgx_service_loadtest.py \
    --service diarize --port 8001 --audio episode.wav \
    --concurrency 1,2,4,8 --duration 60 \
    --vm-ssh homelab-claude --out reports/dgx-diarize-baseline.json

--dry-run validates VM connectivity without sending load (works even while the DGX is down — verified 2026-08-04).

Baseline template (fill one per service)

Concurrency p50 p95 throughput MemAvailable min Shmem peak Shmem Δ errors
1
2
4

Then conclude, per service: - Idle/resident footprint (GB reserved at load) and memory-per-concurrent-request (Δ per +1). - Concurrency ceiling — where latency knees, errors start, or MemAvailable nears the cliff. - /dev/shm behaviour — does Shmem grow with load? Did the incident-fix (set_sharing_strategy('file_system') in pyannote-server) flatten it? - Improvement opportunities — GPU-util headroom (are we GPU- or memory-bound?), batching, max_tokens/audio-chunking, the right per-service concurrency cap + MemoryMax.

Tracking

One GH issue per service (label incident / perf), each capturing its baseline table + opportunities. Umbrella: #1397.