Tutorial
llama.cpp MTP-Fix – Multi-Token-Prediction zuverlässig zum Laufen gebracht
Ein Patch für llama.cpp, der dem Server-Prompt-Cache den fehlenden MTP-Träger einräumt: Checkpoint-Restore funktioniert wieder, Cache-Reuse schlägt nicht mehr still fehl und ein defekter Restore beendet den Server nicht mehr per GGML_ABORT.
Wer llama.cpp mit Multi-Token-Prediction (MTP) und dem Server-Prompt-Cache betreibt, stößt auf einen Bug, der sich erst im Alltag zeigt: Ein Branch auf einen gespeicherten Checkpoint prefillt still komplett von vorne – oder der Server beendet sich mit GGML_ABORT.
Der Grund ist klein, aber fatal: Beim Checkpoint speichert llama.cpp die KV-Zustände von Target- und Draft-Modell, aber nicht den MTP-Träger, die mit der KV-Grenze gekoppelte versteckte Zeile. Der Artikel beschreibt den Bug, den Fix in fünf Dateien und die Verifikation – vom HTTP-Test mit Qwen3.8-27B über den C++-Unit-Test bis zum Mutanten-Test.
Wichtig: Der Patch betrifft
tools/servervon llama.cpp und ist bewusst nicht als vollständiges Upstream-PR verpackt. Er ist gegen Commit2ca15f540476(4. Oktober 2026) gebaut und wurde gegen genau diese Version verifiziert. Bei jedem Upstream-Update sollte der Patch mitgit apply --checkneu geprüft werden, bevor er gebaut wird.
Voraussetzungen
- ein llama.cpp-Checkout bei Commit 2ca15f540476 (oder ein späterer Commit, bei dem der Patch sauber anlegt)
- CMake, Ninja und ein C++20-fähiger Toolchain (verifiziert mit GCC 16.2.1)
- ein Zielmodell mit MTP-Ebenen plus passendes MTP-Draft-Modell (hier: Qwen3.8-27B, mtp-Qwen3.8-27B-Q4_0.gguf)
- GPU-Backend für die volle Verifikation (hier: ROCm/Vulkan auf einer AMD Radeon AI PRO R9700)
- Python 3 für den HTTP-Verifikationstest
Teil 1: Den Fix anwenden und starten
Wenn MTP im Server-Modus einfach zuverlässig mit Checkpoints funktionieren soll, ist der praktische Ablauf im Grunde dieser:
1. Patch holen
Der Patch liegt als einzelne Datei vor:
- mtp-prompt-cache-fix.patch (Referenz-Version aus julianmb/q38rocm, Tag v1.5.0)
Im Projektordner den Patch auf den llama.cpp-Checkout anwenden:
cd llama.cpp git apply --check ../mtp-prompt-cache-fix.patch git apply ../mtp-prompt-cache-fix.patch --check vor dem eigentlichen Anwenden ist der bewusste Schritt: Der Fix liegt in tools/server und in common, genau den Bereichen, die Upstream häufig umstrukturiert. Wenn der Check fehlschlägt, ist der Checkout zu neu oder zu alt für diesen Patch.
Die Provenienz des Patches im Überblick:
- Referenz-Patch: julianmb/q38rocm, mtp-prompt-cache-fix.patch
- Upstream-Übernahme: ROCmFPX-Commit 0ef57fb8 („server: tolerate empty data_spec during checkpoint restore“, 2. September 2026)
- Basis-Commit von llama.cpp: ggml-org/llama.cpp, 2ca15f540476
2. llama.cpp bauen
Standard-CMake-Build mit Test-Ziel:
cmake -S . -B build -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DLLAMA_BUILD_TESTS=ON cmake --build build --target llama-server -j 8 Für die volle Verifikation aus dem Projektordner:
./build.sh Das Skript baut llama-server und den Unit-Test test-recurrent-state-rollback. Die Build-String-Prüfung zeigt danach die Version:
./build/bin/llama-server --version # => version: 0.5.0-dev (build 0, commit 2ca15f540476-mtp-fixed) 3. Server mit MTP und Checkpoints starten
Der Startbefehl für die Verifikation (Qwen3.8-27B, 262k Kontext, RAM-Cache):
./build/bin/llama-server \ cold-long 32,03 s 26.458 Prompt 0 cached READY extension 0,47 s 26.478 Prompt 26.459 cached DONE branch 0,41 s 26.459 Prompt 26.442 cached BRANCH exact-repeat 0,21 s 26.459 Prompt 26.455 cached BRANCH RAM-cache-return 0,45 s 26.459 Prompt 26.455 cached BRANCH Status: PASS # ohne Fix (Mutant: MTP-Serialisierung rausgebaut) cold-long 32,31 s 26.458 Prompt 0 cached READY extension 0,49 s 26.478 Prompt 26.459 cached DONE branch 31,96 s 26.459 Prompt 0 cached BRANCH Status: FAIL (branch: MTP branch/cache reuse missing: cached_tokens=0, required>=26330) Die extension-Variante (gleicher Prompt, etwas länger) nutzt den Cache in beiden Builds – sie geht über den normalen KV-Pfad. Die branch-Variante (ein abweichender Prompt aus demselben Checkpoint) ist der entscheidende Test: Nur sie zwingt den Server, den Checkpoint tatsächlich zu restaurieren. Ohne Fix: voller Re-Prefill in 31,96 Sekunden. Mit Fix: 0,41 Sekunden und 26.442 gecachte Tokens.
Der RAM-cache-return-Request prüft zusätzlich, dass ein evakuiertes und wieder geladenes KV-Set denselben Pfad nimmt wie der direkt gecachte Fall. Auch hier: 26.455 gecachte Tokens.
5. C++-Unit-Test und Mutanten-Test
Neben dem HTTP-Test läuft ein C++-Unit-Test (verification/test-mtp-checkpoint.cpp), der die server_slot-Methoden direkt ohne HTTP-Schicht übt. Er deckt ab:
- MTP-Träger-Serialisierung: get_state/set_state tragen data_spec mit
- gepaarte Checkpoints: Target, Draft und MTP-Zustand werden zusammen geschrieben
- Slot-Kopie und Slot-Reset: copy_prompt_to und reset behandeln den Träger korrekt
- RAM-Restore und -Abrechnung: evakuierte Zustände werden korrekt gebucht
- sechs Cache-Fehlerrouten und zwei Checkpoint-Fehlerrouten: defekte Einträge werfen, statt den Prozess zu beenden
Das Ergebnis in der Log-Datei ist eine Zeile:
PASS: MTP carrier, paired checkpoints, slot copy/reset, RAM restore/accounting, six cache failure paths, and two checkpoint failure paths Der Mutanten-Test ist die Gegenprobe: verification/mutants/libmtp-upstream-no-state.so baut genau die MTP-Serialisierung aus dem Build heraus und stellt damit den Upstream-Verhalten nach. Damit läuft derselbe HTTP-Test gegen denselben Server – und er schlägt erwartungsgemäß an:
FAIL: MTP branch/cache reuse missing: cached_tokens=0, required>=26330 Das Unit-Test-Equivalent: FAIL: MTP carrier must be serialized. Ein Test, der den Mutanten nicht erkennt, wäre kein Test – genau das wird hier geprüft.
6. Ergebnis
Wenn der Patch angewendet und der Server auf der gepatchten Version läuft, gilt:
- MTP im Server-Modus funktioniert zusammen mit --ctx-checkpoints und --cache-ram
- ein Branch aus einem Checkpoint lädt den KV-Zustand, statt ihn von vorne zu prefillen (0,41 s statt 31,96 s im Test)
- ein leeres data_spec im gespeicherten Checkpoint wird beim Restore toleriert und nicht abgebrochen
- ein defekter Checkpoint-Eintrag wirft, wird gefangen und setzt den Slot zurück – der Server läuft weiter
- die Verifikation besteht auf drei Ebenen: HTTP-Test (PASS), C++-Unit-Test (PASS), Mutanten-Test (erkennt das fehlende Feature)
Das ist kein generisches MTP-Performance-Feature, sondern ein gezielter Fix für den Fall, der sonst still im Hintergrund passiert: der Checkpoint-Restore mit MTP. Gerade bei längeren Kontexten und parallelen Anfragen mit gemeinsamen Präfixen entscheidet genau dieser Pfad darüber, ob der Cache nützt oder nicht.
Bericht
Meine Kommentare