fix(ram): nur ein grosses Modell resident (Analyse starb im OOM-Killer)
rkllm_server braucht 6,5 GB von 7,74 GB. Blieb daneben ein anderer Dauerhelfer resident, wurde rkllm beim Laden OOM-getoetet -- bei #101 in allen 5 Startversuchen, weil das Retry nichts freigab. Ausloeser war der warme CT2-Whisper-Daemon, den die 2-Sekunden-Aufnahme davor gestartet hatte. Zweiter Fall: rkllm blieb nach der Analyse fuer immer resident, und der Suchindex startete e5-small daneben. gate.js bekommt eine Freigabe-Registry (registerReleaser/freeRam/ killAndWait, verdrahtet in server.js wie setProbe). Vor jedem rkllm-Versuch und vor dem NPU-Whisper-Lauf werden die anderen Helfer beendet und ihr Prozessende abgewartet; analyzeRecording gibt rkllm vor dem Suchindex frei. Alle Helfer starten faul neu. Verifiziert mit der Abfolge, die scheiterte: 12-s-Clip (CT2-Daemon resident) -> Analyse #101 laeuft durch (221 s, done), rkllm lud in 22 s, kein OOM, danach 5,3 GB frei statt 100 MB.
This commit is contained in:
@@ -248,3 +248,60 @@ swappen seriell unter dem NPU-Lock.
|
||||
**Thermik:** `analyzeRecording()` läuft seit 2026-07-16 unter dem A76-Takt-Cap (`.bak-anacap`) — rkllm
|
||||
zieht neben der NPU ~1,7 CPU-Kerne, eine lange ungedeckelte Gemma-Generierung erreichte **95–97 °C**
|
||||
(lüfterlos, Schutzabschaltungs-Risiko). Gleicher Cap wie `transcribeWav`/`rediarize` (Kap. 03).
|
||||
|
||||
## Speicherhaushalt: nur EIN großes Modell resident (seit 2026-08-07)
|
||||
|
||||
Bis dahin hat jeder Helfer für sich entschieden, wann er lädt und wer dafür weichen muss — und das ging
|
||||
schief. **Gemessene Spitzen (`VmHWM`) auf 7,74 GB Gesamtspeicher:**
|
||||
|
||||
| Prozess | Spitze | Anmerkung |
|
||||
|---|---|---|
|
||||
| `rkllm_server` (Qwen3-4B w8a8) | **6,5 GB** | davon 4,79 GB `unevictable` NPU-Shmem — nicht auslagerbar |
|
||||
| `embed_text.py` (e5-small, Suche) | 1,15 GB | faul, bleibt danach resident |
|
||||
| `transcribe_daemon_cpu.py` (CT2) | 0,90 GB | Kurz-Clip-Pfad, bleibt per Design warm |
|
||||
| `spk_embed` (TitaNet) | 0,70 GB | faul, bleibt danach resident |
|
||||
|
||||
**Zwei Ausfälle am 2026-08-07, beide aus derselben Wurzel** (Aufnahme #101, 34 s, 2 Personen):
|
||||
|
||||
1. Die 2-Sekunden-Aufnahme #100 davor hatte den warmen CT2-Daemon gestartet. Beim anschließenden
|
||||
rkllm-Load war er noch resident → `rkllm_server` wurde in **allen 5 Startversuchen** OOM-getötet,
|
||||
GUI: „KI-Analyse fehlgeschlagen". Ein Retry ohne Aufräumen stirbt zwangsläufig genauso wie der erste
|
||||
Versuch.
|
||||
2. Nach einer Analyse blieb rkllm **für immer** resident (belegt: der Prozess von 10:37 lebte noch um
|
||||
13:27). Der Suchindex am Ende von `analyzeRecording()` startet aber das e5-Modell — 1,15 GB neben
|
||||
6,5 GB → der Kernel erschlug rkllm (13:27:09, `dmesg`). Das war auch der Dauerzustand
|
||||
„nur noch ~100 MB frei", in dem jeder weitere Helferstart Glückssache war.
|
||||
|
||||
⚠️ **zram rettet das nicht:** der komprimierte Swap liegt selbst im RAM. 0,9 GB ausgelagert kosten
|
||||
weiter ~0,3–0,45 GB physisch — und rkllms 4,79 GB Shmem sind ohnehin nicht auslagerbar.
|
||||
|
||||
**Regel jetzt:** wer ein großes Modell lädt, räumt vorher auf. `gate.js` führt dazu eine Registry
|
||||
(`registerReleaser` / `freeRam(keep)`, verdrahtet in `server.js` — dasselbe Muster wie `setProbe`, damit
|
||||
`gate.js` keines der Module importieren muss). Alle Helfer starten faul neu; es geht nur Ladezeit
|
||||
verloren, kein Zustand.
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph REG["gate.js — Registry"]
|
||||
R1["rkllm → llm.stopServer()"]
|
||||
R2["cpu-transcriber → rec.stopTranscribeDaemon()"]
|
||||
R3["text-embedder → emb.stop()"]
|
||||
R4["spk-embedder → spk.stop()"]
|
||||
end
|
||||
A["NPU-Whisper startet<br/>(recorder.js)"] --> FA["freeRam('npu-whisper')<br/>→ beendet ALLE vier"]
|
||||
B["rkllm lädt<br/>(llm.startServer, vor JEDEM<br/>der 5 Versuche)"] --> FB["freeRam('rkllm')<br/>→ beendet die anderen drei"]
|
||||
FA --> REG
|
||||
FB --> REG
|
||||
REG --> W["SIGTERM, auf Prozessende warten,<br/>1,5 s Kernel-Reclaim<br/>(gate.killAndWait)"]
|
||||
W --> LOAD["großes Modell lädt allein"]
|
||||
LOAD --> END["Analyse fertig →<br/>llm.stopServer() VOR dem Suchindex<br/>(analyzeRecording)"]
|
||||
```
|
||||
|
||||
**Verifiziert am 2026-08-07** mit genau der Abfolge, die vorher scheiterte: 12-s-Clip → warmer
|
||||
CT2-Daemon resident → Analyse von #101 → Journal
|
||||
`ram: für rkllm freigegeben — cpu-transcriber, text-embedder, spk-embedder`, rkllm lud in **22 s** (nicht
|
||||
96 s: die Modelldatei lag noch im Seiten-Cache), Analyse in 221 s fertig, kein OOM. Danach **5,3 GB
|
||||
frei** statt 100 MB.
|
||||
|
||||
**Preis:** die nächste Analyse lädt rkllm neu. Der Kurz-Clip-Pfad verliert damit seinen „rkllm bleibt
|
||||
warm"-Vorteil (Kap. 03) — dafür ist die Analyse verlässlich statt schnell-aber-manchmal-tot.
|
||||
|
||||
@@ -3,6 +3,44 @@
|
||||
Chronik der Entwicklungsschritte. **Regel: jede zukünftige Änderung bekommt hier einen Eintrag** (Datum,
|
||||
Was, Warum, betroffene Dateien/Backups).
|
||||
|
||||
## 2026-08-07 (2) — „KI-Analyse fehlgeschlagen" bei einer 34-Sekunden-Aufnahme: zwei Modelle gleichzeitig
|
||||
|
||||
**Symptom:** Aufnahme #101 (34 s, 2 Personen) — Transkript und Sprecherspuren da, aber
|
||||
„KI-Analyse fehlgeschlagen". Also **nicht** dieselbe Ursache wie der Vormittags-Fix: der betraf die
|
||||
Länge, dieser die *Reihenfolge*. Gemeinsam ist nur der OOM-Killer.
|
||||
|
||||
**Ursache 1:** `rkllm_server` braucht 6,5 GB von 7,74 GB. Die 2-Sekunden-Aufnahme #100 unmittelbar davor
|
||||
hatte über den Kurz-Clip-Pfad den warmen CT2-Whisper-Daemon (0,9 GB) gestartet, der per Design resident
|
||||
bleibt. Beim rkllm-Load war er noch da → OOM-getötet, und zwar in **allen 5 Startversuchen**, weil das
|
||||
Retry nichts freigab: `[rkllm] start 5/5 failed: rkllm beendet vor READY`.
|
||||
|
||||
**Ursache 2:** rkllm blieb nach einer Analyse für immer resident (der Prozess von 10:37 lebte um 13:27
|
||||
noch). Der Suchindex am Ende von `analyzeRecording()` startet aber e5-small (1,15 GB) — daneben erschlug
|
||||
der Kernel rkllm (13:27:09). Dasselbe Muster war auch der Dauerzustand „nur ~100 MB frei".
|
||||
|
||||
**Fix:** `gate.js` bekommt eine Freigabe-Registry (`registerReleaser`/`freeRam(keep)`/`killAndWait`,
|
||||
verdrahtet in `server.js` wie schon `setProbe`). Wer ein großes Modell lädt, beendet vorher die anderen
|
||||
Dauerhelfer und wartet auf ihr Prozessende — vor **jedem** der 5 rkllm-Versuche und vor dem
|
||||
NPU-Whisper-Lauf. Dazu gibt `analyzeRecording()` rkllm frei, **bevor** der Suchindex läuft. Alle Helfer
|
||||
starten faul neu; verloren geht nur Ladezeit.
|
||||
|
||||
**Verifiziert** mit genau der Abfolge, die scheiterte: 12-s-Clip (CT2-Daemon resident) → Analyse #101 →
|
||||
`ram: für rkllm freigegeben — cpu-transcriber, text-embedder, spk-embedder`, rkllm lud in 22 s, Analyse
|
||||
„Auto und Budget" in 221 s fertig (`done`), kein OOM, danach **5,3 GB frei** statt 100 MB.
|
||||
|
||||
Dateien: `app/gate.js`, `app/llm.js`, `app/server.js`, `app/recorder.js`, `app/spk.js`, `app/emb.js`
|
||||
(Board-Backups `.bak-ramfree`), Kapitel [05 – LLM-Analyse](05-llm-analyse.md) (neuer Abschnitt
|
||||
„Speicherhaushalt" mit Messtabelle und Diagramm).
|
||||
|
||||
**Offen, getrennt davon (gemessen, nicht gefixt):** dieselbe Aufnahme zeigt 4 Sprecher statt 2, und die
|
||||
Stimmabdruck-Erkennung ist auf ROHEN Kosinus nicht trennscharf — `Sascha` ↔ `Sascha Wolf` (dieselbe
|
||||
Person, zwei Einträge) liegt bei 0,928, aber `Sascha Wolf` ↔ `Johannes` (verschiedene Personen) bei
|
||||
**0,912** und `Sascha` ↔ `Fabio Haase` bei 0,876. Bei `DIAR_VP_MIN` 0,55 passt damit fast jeder auf
|
||||
jeden; eine höhere Schwelle würde nur alles ablehnen. `Jens` ↔ `Jens Kuppler` ist mit Kosinus **1,000**
|
||||
sogar eine exakte Dublette. Der Clustering-Teil hat dieses Problem 2026-07-27 durch Mittelwert-Zentrierung
|
||||
gelöst — die Abdruck-Erkennung arbeitet noch ohne. Dazu kommt die Über-Segmentierung kurzer Clips
|
||||
(diagnostiziert 2026-07-31, Kap. 04).
|
||||
|
||||
## 2026-08-07 — Lange Aufnahmen scheiterten: sherpa-Diarisierung lief in den OOM-Killer
|
||||
|
||||
**Symptom:** Aufnahme #99 (53:56) endete mit „Transkription fehlgeschlagen“ (Badge „KI-Fehler“), beim
|
||||
|
||||
Reference in New Issue
Block a user