Halogen auf Strix Halo
Halogen ist eine proprietäre Inferenz-Engine von peonist-ai, gebaut für gfx1151. Auf dem Ryzen AI Max+ 395 liegt sie beim Prefill klar vor llama.cpp — beim Decode nur knapp. Und sie unterstützt genau ein Sprachmodell. Seit 0.12.3 hat sie bei gleichen Messbedingungen um rund 50 % zugelegt: 2145 t/s Prefill und 66,3 t/s Decode bei 16k Prompt unter 0.18.19. Diese Seite sortiert, was davon für den eigenen Betrieb zählt: gemessene Zahlen, die Entwicklung über die Versionen, die Cache-Fixes, die Fallstricke und der Update-Ablauf.
Einordnung
Lohnt sich, wenn …
- ✓Langer Kontext, viele Turns, Agenten-Fan-out
- ✓Prefill-dominierte Workloads (große System-Prompts, Repo-Kontext)
- ✓Lizenz muss kostenlos und kommerziell nutzbar sein
Lohnt sich nicht, wenn …
- ✕Mehr als ein Modell nötig
- ✕Offene Engine, selbst patchbar, nachvollziehbar
- ✕Kurze Einzel-Chats mit kleinem Kontext
Steckbrief
- ✓Perpetual-Lizenz, kostenlos
- ✓kommerzielle Nutzung erlaubt
- ✓keine Telemetrie
- ·Qwen3.8-Flash-Next
Mehr ist es nicht. Wer ein zweites Modell braucht, bleibt bei llama.cpp oder Ollama.
Leistung: Halogen gegen llama.cpp
| Kennzahl | Kontext | Halogen | llama.cpp | Faktor |
|---|---|---|---|---|
| Prefill | 12k Tokens Prompt, Halogen 0.11.3 | 1.340 t/s | 413 t/s | 3,2× |
| Decode | Token-Ausgabe, Halogen 0.11.3 | 33,5 t/s | 26,1 t/s | 1,28× |
Der eigentliche Gewinn. Agenten hängen am ersten Token — hier entscheidet sich, ob ein Turn mit langem System-Prompt Minuten oder Sekunden kostet.
Spürbar, aber nicht dramatisch. Wer nur chattet, merkt davon wenig; der Unterschied summiert sich erst bei langen Antworten.
Entwicklung: dieselbe Maschine, neuere Versionen
Quellen: halogen-0.12.3-maxtok16384.json (2026-09-21) · halogen-0.14.0-maxtok16384.json (2026-09-26) · halogen-0.17.3-v2.json (2026-10-09) · halogen-0.18.5-v2.json (2026-10-16) · halogen-0.18.7-v2.json (2026-10-17) · halogen-0.18.19-v2.json (2026-10-30)
Prompt-Cache: der eigentliche Grund für die Updates
Determinismus-Bug im Cache
Ein kalter Request lieferte nicht dasselbe Ergebnis wie derselbe Request mit deaktiviertem Cache.
Bei temperature > 0 führte das zu plausibel klingenden Falschantworten. Nichts crasht, nichts loggt — die Antwort ist einfach still falsch.
Cache-Pfad und Nicht-Cache-Pfad verhalten sich identisch.
Cache-Verdrängung bei parallelen Konversationen
Zwei lange parallele Konversationen und Fan-out-Muster (ein Eltern-Agent mit mehreren Kind-Agenten) passten nicht nebeneinander in den Cache.
Bei jedem Turn wurde komplett neu geprefillt: 100 bis 160 Sekunden pro Turn. Eltern und Kinder warfen sich gegenseitig aus dem Cache.
99 Prozent Cache-Treffer in denselben Szenarien.
Cache-Diebstahl bei gleichem System-Prompt
Mehrere Konversationen mit identischem System-Prompt griffen auf denselben Cache-Eintrag zu.
Eine Konversation konnte den Cache-Eintrag einer anderen an sich ziehen, die dann wieder von vorn prefillen musste — typisch bei Agenten, die alle mit demselben Prompt starten.
Jede Konversation behält ihren eigenen Eintrag.
Gemeinsamer System-Prompt bleibt warm
Bei vielen kurzen Konversationen wurde auch der gemeinsame System-Prompt immer wieder verdrängt.
Jede neue Konversation zahlte den Prefill des System-Prompts erneut.
Der Cache hält den gemeinsamen System-Prompt plus den letzten Stand je Konversation. HALOGEN_CACHE_EVICT=0 stellt das alte Verhalten her.
| Version | Bereich | Was passierte |
|---|---|---|
| 0.11.0 | Token-Budget | Engine reserviert selbst Antwortraum (thinking_answer_room) — entschärft das Problem mit den Denk-Tokens gegen max_tokens. |
| 0.11.3 | Cache | Determinismus-Bug behoben (kalter Request ≠ Ergebnis mit deaktiviertem Cache). |
| 0.11.5 + 0.11.7 | Cache | Cache-Verdrängung bei parallelen Konversationen und Fan-out behoben: von 100–160 s pro Turn auf 99 % Treffer. |
| 0.11.6 | Modellformate | Liest zusätzlich K-Quant GGUF, nicht nur das eigene .hgn-Format. |
| 0.11.9 | Speicher | GTT-Leak behoben: 35 GiB blieben nach unsauberem Abbruch im Kernel hängen. |
| 0.12.x | Speicher | Reserviert rund 20 GiB Host-RAM für eine n-gram-Lookup-Tabelle. Die alte Konfiguration mit HALOGEN_MAX_TOK=32768 geht damit nicht mehr auf (Fallstrick „refusing to pin“). |
| 0.13.4 | Agenten | Leere Agent-Turns behoben (HALOGEN_EOS_GUARD=0 = altes Verhalten); 0.13.5/0.13.6: Sessions brachen nach angekündigtem Tool-Call ohne Tool-Call ab. |
| 0.14.0 | Decode | Neuer MTP-Draft-Head (Tiefe 2, HALOGEN_MTP_DEPTH). Gemessen: Decode 51,0 statt 43,5 t/s bei 16k Prompt gegen 0.12.3. |
| 0.15.x | Checkpoint | Checkpoint v2: 62,1 GiB gepinnt statt 68, dazu eine eigene n-gram-Lookup-Datei. Dazu Anthropic-Messages-API (/v1/messages). |
| 0.16.x | Prefill / NPU | Schnellerer Prefill, Abbruch bei langem Prefill in ~1 s, erste NPU-Kleinmodelle (Embedding, Reranker, Moderation, Qwen3.5-2B) — opt-in über HALOGEN_NPU_MODELS. |
| 0.17.0 | NPU / Decode | Bildgenerierung auf der NPU (flux2-klein-4b, 256 oder 512 px, ~8 GB Host-RAM) neben dem Sprachmodell auf der GPU. Zwei parallele Konversationen spekulieren gemeinsam. |
| 0.17.1–0.17.3 | Tools / Decode | Tool-Call-Fixes (Schleife bei JSON-Schema mit Thinking, nullable Strings), schnellerer Decode, HALOGEN_MTP_DEPTH ist ungesetzt automatisch. Gemessen 0.17.3: 1908 t/s Prefill, 58,6 t/s Decode bei 16k. |
| 0.18.3–0.18.5 | Decode / Prefill | Antworten stoppen am zweiten End-of-Text-Token statt bis max_tokens weiterzulaufen, schnellerer Prefill, MTP-Kette bricht ab, sobald der Draft-Head unsicher ist, schnellere Strict-Tool-Calls (HALOGEN_GRAMMAR_DRAFT). Gemessen 0.18.5: 2024 t/s Prefill, 62,8 t/s Decode bei 16k. |
| 0.18.6–0.18.7 | Decode | Gesampelter Decode schneller (Draft-Head schlägt bis zu sieben statt drei Tokens vor, HALOGEN_SAMPLED_DEPTH/HALOGEN_SAMPLED_GATE = alt), Fix: kurze Antworten seit 0.18.5 langsamer (#186), HALOGEN_CHAT_TEMPLATE tauscht das Chat-Template. Gemessen 0.18.7: 1990 t/s Prefill, 63,9 t/s Decode bei 16k — gleichauf mit 0.18.5. |
| 0.18.8–0.18.11 | Cache / Decode | Langer Verlauf behält seinen Prompt-Cache (HALOGEN_CACHE_EVICT=1 = alt), opt-in 8-bit-KV-Cache (HALOGEN_KV8), schnellerer Prefill, drei bis vier parallele Requests dekodieren gemeinsam (HALOGEN_SPEC_GROUP=0 = aus). Fixes: Endlosschleife im KV-Pool (#190), Zweit-GPU (#188), Grammar-Crash bei rekursivem Schema (#192). |
| 0.18.12–0.18.19 | Prefill / Tools / Stabilität | Schnellerer Prefill und Decode, kurze Requests starten während eines langen Prompts, ein NPU-Fehler reißt den Server nicht mehr mit, Answer-Guard gegen Abbruch in Codeblöcken, Tool-Call-Argumente mit Markup kommen vollständig an (#209, #216, #221), Claude-Code-Sessions behalten den Prompt-Cache beim Nachladen von Tools, ggml-orgs offizielles GGUF lädt (#217). 0.18.15 (Auto-Keep) hier übersprungen. Gemessen 0.18.19: 2145 t/s Prefill, 66,3 t/s Decode bei 16k. |
Fallstricke im Alltag
KV-Pool kleiner als Kontext
Der Container beendet sich beim Start mit Exit 1. Ohne Meldung, was genau fehlt.
HALOGEN_KV_POOL_POSITIONS muss größer oder gleich HALOGEN_CTX sein. Beide Werte gehören zusammen geprüft, bevor der Start überhaupt Sinn ergibt.
Denk-Tokens fressen das Antwortbudget
Standard-reasoning_effort ist xhigh. Die Denk-Tokens zählen gegen max_tokens — ohne chat_template_kwargs kommt leerer Content zurück.
Entweder reasoning_effort explizit setzen und max_tokens großzügig bemessen, oder ab 0.11.0 auf thinking_answer_room verlassen: die Engine reserviert den Antwortraum selbst.
localhost löst auf IPv6 auf
Clients bekommen Connection error. Auf vielen Distributionen löst localhost nur auf ::1 auf, die Engine lauscht aber auf IPv4.
In Client-Konfigurationen 127.0.0.1 statt localhost schreiben. Kostet eine Zeile Konfiguration und spart die halbe Stunde Fehlersuche.
Das model-Feld wird ignoriert
Beliebige Werte im model-Feld des Requests funktionieren kommentarlos. Der Client glaubt, mit einem anderen Modell zu reden.
Nicht als Modell-Weiche missbrauchen wollen. Was geliefert wird, steht in der Server-Konfiguration, nicht im Request.
Kein Parallelbetrieb mit llama.cpp
Halogen und llama.cpp schließen sich gegenseitig aus, wenn beide denselben Port und den Grafikspeicher beanspruchen.
Umsteigen heißt abschalten. Ein Side-by-Side-A/B über beide Engines hinweg erfordert getrennte Ports und genug Speicher für beide — oder zwei Startvorgänge nacheinander.
podman stop tötet zu früh
Nach einem Update startet der neue Container nicht: „Address already in use“. Der Rollback scheitert anschließend an „container state improper“.
podman stop wartet standardmäßig nur 10 s, Halogen braucht zum Freigeben der gepinnten Gewichte länger. Mit stop -t 120 stoppen und vor dem Neustart aktiv prüfen, dass der Port wirklich frei ist (ss), nicht nur der Containerstatus.
„checkpoint: refusing to pin“ nach Update
Ab 0.12 startet Halogen mit der bisherigen Konfiguration nicht mehr oder läuft knapp an der Speichergrenze.
Die n-gram-Lookup-Tabelle braucht eigenen Host-RAM. HALOGEN_MAX_TOK von 32768 auf 16384 senken — gemessen ohne Verlust (Prefill 1404 t/s bei 16k). Dauerkonfiguration ins Startskript, nicht in eine Update-Variable, sonst geht sie beim nächsten Update verloren.
Lange Prompts kriechen, Platte ist busy
Prefill bei langen Prompts bricht ein, die NVMe zeigt Dauerlast, obwohl das Modell geladen ist.
Die Lookup-Tabelle liegt im Page-Cache und konkurriert mit dem BIOS-Carve-out der iGPU. Laut Halogen-README das Carve-out im BIOS auf das explizite Minimum stellen, nicht auf Auto.
Benchmark misst den Cache statt des Prefills
Der zweite und dritte Lauf desselben Prompts sind um Größenordnungen schneller als der erste.
Halogens Prompt-Cache holt den gespeicherten Zustand zurück. Für Prefill-Messungen jedem Lauf eine eindeutige Kennung voranstellen (strix-bench macht das selbst), sonst misst man das Zurückholen eines Zustands.
Betrieb: Update im laufenden Betrieb
- 1Neues Image ziehen
Läuft 4,5 Minuten, ohne den laufenden Container zu stören.
- 2Alten Container parken statt löschen
Hält den Rollback verfügbar, bis der neue bewiesen ist.
- 3Neuen Container starten
/health antwortet — Version, Slots, Pool-Größe und Kontext abgleichen.
- 4Echten Completion-Request absetzen
Health allein reicht nicht. Es muss ein echter Prompt durch, der Content muss stimmen.
- 5Bei Fehlern zurückrollen
Den geparkten Container wieder starten, Problem am neuen Image suchen.
| Kennzahl | Wert | Kontext |
|---|---|---|
| Downtime beim Rolling-Update | 12–26 s | Gemessen von altem Container weg bis neuer Container bedienbar, inklusive Smoke-Test. 26 s bei den ersten Updates, 12 s beim Update auf 0.17.3. |
| Image-Pull | 4,5 min | Läuft parallel zum laufenden Betrieb, unterbricht nichts. |
| Health-Endpunkt | /health | Liefert Version, Slots, Pool-Größe und Kontext. |