Release Notes versprechen, dass wachsende Prefixe wiederverwendet werden. Ob das für dein Modell gilt, sagen sie nicht.

vllm-mlx 0.5.0 brachte im September einen neuen Schalter: --prefix-trie-cache. Die Release Notes sagen, wachsende Gesprächspräfixe würden wiederverwendet. Für eine n8n-Kette oder eine Agent-Schleife wäre das der eigentliche Hebel, denn dort beginnt jeder Aufruf mit demselben langen Anfang aus Systemprompt, Werkzeugen und Verlauf. Ohne Cache zahlt man das Prefill bei jedem Schritt neu, und genau dort entsteht die Wartezeit, nicht beim Generieren.

Ich wollte wissen, wie viel das in einer realistischen Kette ausmacht. Dieser Artikel beschreibt die Messung, ihr Ergebnis (bei meinem Modell: nichts), die Kontrolle mit einem anderen Modell und den Vergleich mit Ollama 0.40, das auf Apple Silicon jetzt MLX nutzt.


Der Messaufbau

Mac M2 Max mit 96 GB, vllm-mlx 0.5.0 in einer eigenen virtuellen Umgebung, Modell Qwen3.6-35B-A3B in 4 Bit. Zwei Arbeitslasten, in allen Läufen Token für Token gleich, jeweils per Streaming gemessen bis zum ersten Token (TTFT), mit abgeschaltetem Denkmodus und 24 Ausgabe-Token:

→ Die n8n-Kette: zehn Dokumente, je drei unabhängige Aufrufe (Klassifikation, Extraktion, Antwortentwurf). Systemprompt mit rund 2.800 Token (Kategorien, Regeln, Beispiele, Werkzeugbeschreibungen), dazu ein Dokument mit 560 bis 690 Token. Gemeinsam sind Systemprompt und Dokument, die Aufgabe am Ende wechselt.
→ Die Agent-Schleife: vier Schleifen mit je acht Runden. Der Verlauf wächst von 3.500 auf 5.900 Token, jede Runde ist ein neuer Request mit dem ganzen bisherigen Gespräch.

Das sind 62 Requests je Konfiguration. Die Kette ist synthetisch, nach dem Muster meiner Workflows, nicht mein Produktivbetrieb. Jede Konfiguration lief mit frisch gestartetem Server, also kaltem Cache.

Ein Hinweis zur Fragestellung: In vllm-mlx 0.5.0 ist das normale Prefix-Caching schon standardmäßig an. „Ohne Trie“ heißt also nicht „ohne Cache“. Ich habe deshalb drei Varianten gemessen: Cache ganz aus (--disable-prefix-cache), Standard, und Standard plus Trie.


Das Ergebnis bei Qwen3.6-35B: kein Unterschied

Median der TTFT in Sekunden, Cache aus / Standard / mit Trie:

→ n8n-Kette: 3,71 / 3,72 / 3,72
→ Agent-Schleife: 5,01 / 5,00 / 5,00

Mit --continuous-batching dasselbe Bild: 3,87 / 3,85 / 3,86 Sekunden in der Kette, 5,02 / 5,01 / 5,02 in der Schleife.

Der Server liest bei jedem Aufruf den ganzen Prompt neu, mit rund 950 Token pro Sekunde. Pro 1.000 Prompt-Token sind das etwa eine Sekunde Wartezeit. In der Schleife steigt die TTFT von 3,7 Sekunden in Runde 1 auf 6,2 Sekunden in Runde 8, und drei Nodes hintereinander warten gut elf Sekunden nur auf das Prefill.


Warum: Das Modell hat einen anderen Cache-Typ

Das Log von vllm-mlx meldet bei jedem Request: „System KV cache SKIP … non_kv_cache_class“. Qwen3.6 besteht in der Konfiguration aus 40 Schichten, davon sind 30 lineare Aufmerksamkeit und nur 10 volle Aufmerksamkeit. Der Zwischenspeicher dieser Schichten besteht nicht aus reinen KV-Einträgen, und die Cache-Zweige der Engine lassen solche Modelle aus. Im Quellcode ist das eine Sperre, die vor dem Trie greift. Der Schalter ist zwar aktiv („enabled, max_entries=32“), kann hier aber nichts verkürzen.

Im Batched-Modus sieht es genauso aus: Die Cache-Statistik zeigt 63 Abfragen, 0 Treffer und 0 gespeicherte Einträge. Gespeichert wurde nie etwas.


Die Kontrolle: Ein klassisches Modell

Damit ich sicher sein konnte, dass der Aufbau nicht schuld ist, habe ich dasselbe mit Qwen3-Coder-30B-A3B in 8 Bit gemessen, einem Modell mit klassischer Aufmerksamkeit.

Dort greift der normale Systemprompt-Cache, auch ohne Flag und sogar mit --disable-prefix-cache: 61 von 62 Requests waren Treffer. Der erste Aufruf kostete rund 5,5 Sekunden, alle weiteren in der Kette etwa 1,2 Sekunden. Dieser Cache lässt sich nicht abschalten.

Der Trie-Cache brachte aber auch dort nichts: kein einziger „Prefix trie cache HIT“ in 94 Requests, gleiche Zeiten wie ohne Flag, auch dann, wenn die Schleife die echten Modellantworten in den Verlauf zurückspielt. In der Agent-Schleife wächst die TTFT dort von 1,2 auf 5,2 Sekunden, weil nur der Systemprompt wiederverwendet wird, nicht das Gespräch. Woran das liegt, habe ich nicht herausgefunden. Es kann an meinem Aufbau liegen.


Ollama 0.40 mit MLX: Es rechnet weniger

Ollama nutzt ab 0.40 auf Apple Silicon für Modelle im MLX-Format standardmäßig den MLX-Runner. Ich habe den Release-Candidate 0.40.0-rc5 parallel zur installierten App in einem eigenen Ordner gestartet, mit einem Klon meiner Modelle, damit der Modellspeicher unberührt blieb. Verglichen habe ich dieselbe Modellfamilie: Ollama mit qwen3.5:35b-a3b-coding-nvfp4 gegen vllm-mlx 0.5.0 mit Qwen3.5-35B-A3B in 4 Bit. Es sind nicht dieselben Gewichte (Coding-Variante in NVFP4 gegen Standardmodell in 4 Bit), aber dieselbe Architektur.

Median der TTFT, Ollama gegen vllm-mlx:

→ Agent-Schleife: 0,53 s gegen 4,8 s. Bei Ollama bleibt es ab Runde 2 bei 0,52 bis 0,54 s, bei vllm-mlx steigt es von 3,5 auf 6,0 s
→ n8n-Kette: 0,80 s gegen 3,5 s

Ollama rechnet dabei nicht schneller: Kalt liegen beide bei 850 bis 1.000 Token pro Sekunde. Aber Ollama verarbeitet offenbar nur den neuen Teil jeder Runde, rund 340 Token, und die Zeiten passen dazu. vllm-mlx liest bei diesem Hybridmodell jedes Mal alles.

Eine Lücke gibt es auch bei Ollama. In der Kette wird der Systemprompt über verschiedene Dokumente hinweg wiederverwendet. Das Dokument aus Aufruf 1 wird aber nicht für Aufruf 2 wiederverwendet (0,84 s), erst Aufruf 3 profitiert von Aufruf 2 (0,27 s). Die Aufrufe unterscheiden sich nur in der Aufgabe am Ende. Warum das so ist, weiß ich nicht.


Parallelität und welche Modelle über MLX laufen

Parallel: Vier gleichzeitige Anfragen laufen nacheinander, das erste Token kommt nach 4,1 / 5,1 / 5,9 / 6,9 Sekunden. Der Speicher des Ollama-Prozesses wächst nicht: 18,4 GB vorher und nachher.

Welche Modelle: Ollama führt jetzt je Modell einen Runner. Das MLX-Modell steht auf mlx, meine GGUF-Modelle aus der Bibliothek (gemma4:26b, gpt-oss:20b, qwen3:8b) stehen auf ggml. Geladen habe ich gemma4:26b: Es lief über llama.cpp, obwohl die Release Notes gemma4 als MLX-Architektur nennen. Für MLX braucht man also die MLX-Fassung des Modells, nicht den gewohnten Tag.

Außerdem startet beim ersten Laden eines GGUF in 0.40 eine einmalige Kompatibilitäts-Migration. Bei gemma4:26b dauerte sie 27 Sekunden und verlangte 23 GB freien Platz. Wer viele Modelle hat, sollte das vor dem Update einplanen.


Was ich daraus mitnehme

→ Release Notes sind keine Zusage für dein Modell. „Wachsende Prefixe werden wiederverwendet“ gilt in meiner Messung nicht für ein Hybridmodell wie Qwen3.6, und nicht einmal bei einem klassischen Modell konnte ich Treffer sehen.
→ Bei Hybridmodellen ist der Prompt in vllm-mlx 0.5.0 ein linearer Kostenfaktor. Kurzer Systemprompt, kleine Dokumente, und die Rechnung „etwa eine Sekunde je 1.000 Token“ im Kopf behalten.
→ Bei Agent-Schleifen entscheidet das Cache-Verhalten mehr als Tokens pro Sekunde. Das gilt für Ollama mit MLX bei diesem Modell klar zugunsten von Ollama. Warum Tokens pro Sekunde allein wenig über die Wartezeit sagen, steht auch in Die GPU-Kiste, Teil 4.
→ Messen, nicht annehmen. Das Skript dafür ist kurz, und der Befund hätte sich im Datenblatt nie gezeigt.


Vorbehalte und Stand

Ein Rechner, Einzelläufe, eine synthetische Kette. Antwortqualität habe ich nicht verglichen, und LM Studio, das ich in n8n nutze, auch nicht. Getestet ist nur die Familie Qwen3.5 und 3.6 (plus ein klassisches Kontrollmodell).

Wenn du lokale Modelle in eigene Abläufe einbauen willst und wissen möchtest, was auf deiner Hardware realistisch ist, sieh dir meine Leistung Lokale KI an.

Die Messungen stammen vom 6. Oktober 2026 mit vllm-mlx 0.5.0 und Ollama 0.40.0-rc5. Inzwischen sind vllm-mlx 0.6.0 und Ollama 0.40.7 erschienen. Ob sich an den Ergebnissen etwas geändert hat, habe ich nicht gemessen.