# SEKTION 02 — GRAPH UND SPEICHER

> Bestehender Code: `kern/graph.py` (läuft, getestet).
> Beispiel: `grafen/t2i_1024.json` — Nachbau der echten Messung.

---

## 2.1 Das Graph-Format

```json
{
  "name": "t2i 1024",
  "knoten": {
    "unet": { "typ": "modell.unet_laden",
              "werte": { "datei": "z-image-turbo-Q3_K_M.gguf", "resident": true } },
    "dec":  { "typ": "vae.dekodieren",
              "werte": { "praezision": "bf16", "verdraengen": "nein",
                         "bei_engpass": "ausweichen" },
              "verbindungen": { "latent": ["samp","latent"], "vae": ["vae","vae"] } }
  }
}
```

- `werte` — alles, was der Node einstellbar hat, **inklusive** `geraet`,
  `praezision`, `resident`, `verdraengen`, `bei_engpass`.
- `verbindungen` — `[quell-knoten, ausgang]`.
- Schlüssel der Knoten sind **sprechende Namen** (`unet`, `dec`), keine
  Nummern. Wer einen Graphen liest, soll ihn verstehen.

**Die geltenden Werte kommen aus genau zwei Quellen:** Standard des
Node-Typs, überschrieben vom Graphen. `graph.werte_von()` ist die eine
Stelle, die das auflöst. Es gibt keine dritte Quelle — das ist das Prinzip.

## 2.2 Prüfen — ehrlich und vollständig

`graph.pruefe()` liefert Klartext-Meldungen (leere Liste = in Ordnung):

| Fall | Meldung |
|---|---|
| unbekannter Typ | `[dec] unbekannter Node-Typ: 'vae.zauber'` |
| erfundener Wert | `[dec] vae.dekodieren: 'quantencomputer' gibt es nicht. Möglich: auto, gpu, cpu` |
| Zahl außerhalb | `[samp] sampler.ksampler: 999 liegt über dem Maximum 200` |
| Verbindung ins Leere | `[dec] … zeigt auf Knoten 'xy', den es nicht gibt` |
| falscher Ausgang | `[dec] … will 'bild' von samp, das dort nicht herauskommt (vorhanden: latent)` |
| Pflicht fehlt | `[dec] … Eingang 'vae' ist nicht verbunden` |
| Kreis | `Der Graph dreht sich im Kreis: a → b → a` |

**Nie still korrigieren.** Ein gekappter Wert ist ein Fehler, den niemand
findet.

## 2.3 Der Speicher-Plan — die Kernaufgabe dieser Sektion

`graph.speicherplan(graph, vram_gb, groessen_gb)` rechnet **vorher** durch,
was wann im VRAM liegt.

Warum vorher (der Nutzer, 12.08.):
> *„Wenn der Shared Memory bei Windows 32 GB ist und das hin und her zieht,
> okay — aber warum dauert es fünfmal, zehnmal so lang?"*

Windows/WDDM lagert bei VRAM-Überlauf **still** in den geteilten
Systemspeicher aus. Es läuft weiter, wird um ein Vielfaches langsamer, und
niemand erfährt davon. Ein Programm, das seinen Speicher plant, landet dort
nicht aus Versehen.

Der Plan liefert je Schritt: `braucht_gb` · `belegt_vorher_gb` · `passt` ·
`raeumt` (wen) · `hinweis`. Und in der Summe: `raeumungen`, `absagen`.

Ausgabe im Klartext (`graph.bericht()`):

```
DEINE ART (Decode darf nichts verdrängen, 16 GB)
  Räumungen: 0 · Absagen: 0
  Kein Modell muss weichen – das ist der Zustand, den wir wollen.

WIE COMFYUI (darf räumen, VRAM knapp, 9 GB)
  samp → räumt clip — genau das kostet Zeit (gemessen: ~6 s pro Bild)
  Räumungen: 1
```

## 2.4 Was `bei_engpass` bedeutet

| Wert | Verhalten |
|---|---|
| `ausweichen` | bewusst auf CPU rechnen — geplant und messbar, **Standard** |
| `stueckeln` | in Happen zerlegen, die passen (Kacheln, Abschnitte) |
| `ablehnen` | ehrlich absagen statt heimlich kriechen |
| `auslagern` | geteilten Systemspeicher zulassen — **nur auf ausdrückliche Ansage** |

`auslagern` ist nie Standard. Das ist der ganze Punkt.

## 2.5 Was noch fehlt (in dieser Sektion zu bauen)

- [ ] **Echte Modellgrößen** statt geschätzter: Dateigröße + Quantisierung
      lesen, Aktivierungs-Bedarf aus Auflösung × Stapel abschätzen. Ohne
      echte Zahlen ist der Plan eine Rechnung mit Platzhaltern.
- [ ] **Plan gegen Wirklichkeit prüfen:** nach einem echten Lauf den
      gemessenen VRAM-Verlauf (`messe_comfy.py --vram` kann das) gegen die
      Vorhersage halten. Ein Plan, der nicht nachgeprüft wird, ist geraten.
- [ ] **Lebensdauer statt nur `resident`:** ein Modell, das später im Graphen
      nochmal gebraucht wird, darf nicht dazwischen fliegen. Aus dem Graphen
      ableitbar — er weiß, wer wen wann braucht.
- [ ] **Mehrere Karten** (`geraet: gpu:1`) vorbereiten, nicht bauen.

## 2.6 Checks für diese Sektion

- [ ] Jede Fehlerart aus 2.2 wird erkannt und **im Klartext** benannt *(steht)*
- [ ] Kreise werden gemeldet, nicht endlos gelaufen *(steht)*
- [ ] `verdraengen: nein` → Plan räumt nichts *(steht)*
- [ ] knapper VRAM + `verdraengen: ja` → Plan räumt *(steht)*
- [ ] passt ein Schritt nicht mal auf die leere Karte → Absage *(steht)*
- [ ] `auslagern` ist nie Standard *(steht)*
- [ ] NEU: Vorhersage vs. gemessener Verlauf weicht weniger als X % ab
