Lokale LLM-Workstation: Von 20 auf 60 Tokens pro Sekunde mit zwei GPUs
Eine gebrauchte Xeon-Workstation mit zwei RTX 5060 Ti, Qwen 27B, Tensor-Parallelität und NCCL: Regeln für die Hardwarewahl, echte Geschwindigkeitsgewinne und die Amortisationsrechnung im Vergleich zu API-Preisen.
Ein Jahr Experimente mit lokaler Inferenz lehrt einiges, vor allem Enttäuschung. Selbst mit reichlich Optimismus gewinnt das Cloud-Abo, sobald echte Arbeit erledigt werden muss. In letzter Zeit gibt es jedoch Druck in die andere Richtung. Abos kaufen von Monat zu Monat weniger Arbeit. Die Zeiten, in denen man GitHub Copilot oder Cursor nutzen konnte, ohne aufs Budget zu achten, wirken weit weg, und Geld, das früher einen Monat reichte, kann an einem Tag weg sein. Das reicht, um ein lokales Setup ernst zu nehmen. Im Folgenden geht es darum, wie aus einem wackeligen Stack mit 20 Tokens pro Sekunde eine zuverlässige Workstation wurde, die im Schnitt 60 Tokens pro Sekunde liefert und in der Spitze deutlich mehr.
Hardware, die Sinn ergibt
Das Internet ist voll von Leuten, die mehrere Mac Pros mit Hunderten Gigabyte Unified Memory stapeln, Modelle auf einem Raspberry Pi laufen lassen (das hat seinen Platz, aber dieser Platz ist Edge-AI) oder mit einem Token pro Minute prahlen, das aus einer SSD gequetscht wurde. Das ist Zirkus. Die eigentlichen Regeln sind einfacher:
- VRAM-Kapazität kommt zuerst. Modell und Kontext müssen in den VRAM passen, und 32 GB oder 48 GB sind heute die erschwinglichen Sweet Spots.
- Zusätzliche GPUs dienen vor allem dazu, mehr unterzubringen, nicht der Geschwindigkeit. Eine zweite Karte bringt VRAM für größere Modelle und längeren Kontext, aber die Aufteilung der Arbeit verursacht Kommunikationsaufwand, also rechnen Sie mit einer leichten Verlangsamung statt einer Beschleunigung.
- Bei mehreren Karten zählt die Topologie. Suchen Sie GPU-zu-GPU-Verbindungen mit hoher Bandbreite und niedriger Latenz, idealerweise hinter einem gemeinsamen PCIe-Switch oder zumindest über CPU-angebundene Lanes. Ein Weg über den Chipsatz ist viel schlechter, besonders bei Tensor-Parallelität.
- Strom und Kühlung gehören zum Design. Zwei oder drei GPUs ziehen viel Leistung und geben ebenso viel Wärme ins Gehäuse ab, daher zählen Netzteilleistung, Luftstrom, Slot-Abstand und GPU-TDP.
Der zweite Punkt ist auf Consumer-Mainboards schwer zu erfüllen. Zwei PCIe-Slots auf dem Papier garantieren keinen guten Weg zwischen den Karten, und Boards, die ihn bieten, sind nicht billig.
Die Alternative sind gebrauchte Business-Workstations. Viele kommen mit Xeon-CPUs, vielen PCIe-Lanes und serienmäßigen Netzteilen ab 1 kW, weil sie für mehrere professionelle GPUs gebaut wurden. Zwei oder drei Karten einzubauen ist dort normal, und oft ist das viel günstiger als eine vergleichbare moderne Consumer-Plattform. Es gibt Kompromisse: älterer DDR4, PCIe Gen4 oder sogar Gen3 und CPUs, die einige Generationen zurückliegen.
Inferenz unterscheidet sich stark vom Gaming, aber veröffentlichte PCIe-Skalierungsbenchmarks für die RTX 5090 zeigen, dass selbst sehr schnelle GPUs bei reduzierter Bandbreite überraschend wenig Leistung verlieren, und das reichte, um es zu versuchen. Am Ende entscheidet fast alles die Wirtschaftlichkeit.
Die Wahl fiel auf zwei NVIDIA RTX 5060 Ti mit 16 GB, der günstigste Weg zu 32 GB VRAM mit hervorragender Energieeffizienz, in einer Xeon-Workstation aus den oben genannten Gründen. Eine Karte passte physisch nicht, obwohl Slots und Lane-Aufteilung stimmten, also wurde sie mit einem PCIe-Riser und einem Kabelbinder am Gehäuse befestigt. Nennen wir es polnisches Kabelbinder-Engineering.
Modelle, die passen
Diese Hardware schafft eine kurze Liste von Modellen, jedes mit einem Kontextfenster von 160k bis 256k bei quantisiertem KV-Cache. Zwei von Google: gemma-4-26b-a4b und ihre QAT-Variante. Zwei von Qwen: qwen3.6-35b-a3b und qwen3.6-27b in q4_k_s.
Das tägliche Arbeitspferd wurde Qwen mit 27B, und nicht nur aus persönlicher Vorliebe. Rankings und Benchmarks können täuschen, aber wenn man ein paar durchgeht, fallen zwei Dinge auf. Nur wenige Modelle laufen auf so bescheidener Hardware, und Qwen führt diese Kategorie mit deutlichem Abstand an. Außerdem ist es eines der wenigen, die mit kleinen Frontier-Modellen mithalten können, also mit solchen, die bereits bewiesen haben, dass sie kleine, klar definierte Aufgaben zuverlässig erledigen.
Angesichts der Kontextgröße und der Fähigkeiten nutzt man es am besten in einer Agenten-Orchestrierung, gepaart mit einem größeren Cloud-Modell, das die Arbeit plant und einzelne Aufgaben verteilt. Vollständig oder auch nur teilweise autonomes Programmieren ist für lokale Inferenz allein noch außer Reichweite. Ein hybrides Setup liefert dagegen wirklich gute Ergebnisse.
Software und warum der erste Versuch langsam war
Zuerst kam LM Studio, weil es einfach ist. Es eignet sich hervorragend, um zu experimentieren und die verschiedenen Optionen von llama.cpp und den Modellen zu verstehen. Es hilft auch beim Debuggen der Integrationsschicht zwischen Tools, die die bereitgestellten Modelle nutzen. Es ist nicht immer offensichtlich, ob Einstellungen, die über etwas wie OpenCode übergeben werden, die Anfragen überhaupt erreichen, und LM Studio lässt sie einen ansehen, Lücken finden und korrigieren.
Mit Qwen3.6-27B lieferte es aber im Schnitt etwa 20 Tokens pro Sekunde, selbst mit aktiviertem Flash Attention, und die Stabilität ließ zu wünschen übrig: Immer wieder gab es seltsame Timeouts.
Die zwei Änderungen, die funktionierten
Tensor-Parallelität
Laut Changelog von 0.4.15 ist CUDA-Tensor-Parallelität inzwischen in LM Studio angekommen, aber das war nicht immer so, und die hier getestete vorgefertigte Runtime ließ trotzdem Leistung liegen. Zuerst: Was ist das?
Der traditionelle Multi-GPU-Ansatz legt einige Layer auf GPU 0, andere auf GPU 1 und reicht die Aktivierungen von einer zur anderen weiter:
Model
│
┌────────┴────────┐
│ │
GPU 0 GPU 1
layers 0–N layers N–end
│ │
└───────→─────────┘
Damit lässt sich ein Modell ausführen, das nicht auf eine Karte passt, mehr nicht. Für einen einzelnen Stream arbeiten die beiden GPUs nie gemeinsam an einem Layer. Die Arbeit läuft als Kette: GPU 0 verarbeitet einen Teil der Layer, die Aktivierungen wandern weiter, GPU 1 erledigt den Rest.
Tensor-Parallelität teilt stattdessen die Berechnung jedes Layers auf die GPUs auf:
One layer
│
┌───────┴───────┐
│ │
GPU 0 GPU 1
part of tensor part of tensor
│ │
└───────┬───────┘
│
next layer
Der Unterschied ist im Multi-GPU-Leitfaden im llama.cpp-Repository beschrieben. Pipeline-Parallelität, der Modus layer, legt verschiedene Layer auf verschiedene GPUs und schickt Tokens nacheinander hindurch. Sie minimiert den Datenverkehr zwischen den Karten, braucht zum Skalieren aber viele Tokens gleichzeitig in Bearbeitung. Tensor-Parallelität, der Modus tensor, teilt jeden Layer auf die GPUs auf und führt pro Layer mehrere kartenübergreifende Reduktionen aus. Sie kann jede Last parallelisieren, hängt aber stark von der Geschwindigkeit der Verbindung ab. Der Pipeline-Modus maximiert den Batch-Durchsatz, der Tensor-Modus minimiert die Latenz.
Als beide GPUs am selben Layer arbeiteten, erreichte Qwen3.6-27B etwa 50 Tokens pro Sekunde. Das schafft aber ein neues Problem. Wenn zwei GPUs jeden Layer gemeinsam verarbeiten, müssen sie ständig miteinander kommunizieren.
NCCL
NVIDIAs NCCL, die Collective Communications Library, wurde genau für diese Art von GPU-zu-GPU-Austausch gebaut. Operationen wie AllReduce bilden die Grundlage verteilter GPU-Arbeit, und ohne effiziente kollektive Schicht kann Tensor-Parallelität zu viel Zeit mit dem Verschieben von Daten statt mit Rechnen verbringen.
Die Überraschung war, dass in der vorgefertigten llama.cpp-Runtime der getesteten LM-Studio-Version die NCCL-Unterstützung deaktiviert war. Tensor-Parallelität braucht NCCL nicht zwingend, weil llama.cpp die nötigen Reduktionen mit eigenem Code ausführen kann, aber NCCL bietet für diesen Verkehr ein optimiertes Backend.
Folgendes passiert. Bei Tensor-Parallelität tauschen die GPUs Zwischenergebnisse aus, zum Beispiel nach einem Teil einer Matrixmultiplikation:
Matrix operation
│
┌─────────┴─────────┐
│ │
GPU 0 GPU 1
computes part computes part
│ │
└─────────┬─────────┘
│
AllReduce
│
combined result
AllReduce kombiniert die Teilergebnisse aller GPUs und gibt jeder das gemeinsame Ergebnis zurück. llama.cpp hat eine eigene Implementierung, die eingebaute Variante funktioniert also. Hier war das AllReduce von NCCL schneller.
Ein paar Stunden später
Der mühsame Teil (fehlende Bibliotheken unter Fedora, Kompilieren, Jonglieren mit llama- und Modellparametern) lohnt keine ausführliche Beschreibung, auch weil sich diese Software-Ecke sehr schnell verändert. Das Ergebnis in Durchschnittswerten:
- Aufteilung nach Layern: etwa 20 Tokens pro Sekunde
- Tensor-Parallelität mit eingebautem AllReduce: etwa 50
- Tensor-Parallelität mit NCCL: etwa 60
Die Spitzen liegen höher. Ein llama.cpp-Log eines Durchlaufs zeigt Prompt-Verarbeitung bei etwa 950–970 Tokens pro Sekunde und Dekodierung bei etwa 78–84 Tokens pro Sekunde:
113.07.960.883 I slot print_timing: id 0 | task 972 | prompt processing, n_tokens = 4810, progress = 0.79, t = 4.96 s / 969.87 tokens per second
113.08.772.504 I slot print_timing: id 0 | task 972 | prompt processing, n_tokens = 5580, progress = 0.92, t = 5.77 s / 966.90 tokens per second
113.09.409.053 I slot print_timing: id 0 | task 972 | prompt processing, n_tokens = 6092, progress = 1.00, t = 6.41 s / 950.75 tokens per second
...
114.19.200.672 I slot print_timing: id 0 | task 972 | n_decoded = 5384, tg = 77.34 t/s, tg_3s = 83.57 t/s
114.22.229.460 I slot print_timing: id 0 | task 972 | n_decoded = 5639, tg = 77.63 t/s, tg_3s = 84.19 t/s
114.25.259.925 I slot print_timing: id 0 | task 972 | n_decoded = 5894, tg = 77.89 t/s, tg_3s = 84.15 t/s
114.28.274.967 I slot print_timing: id 0 | task 972 | n_decoded = 6149, tg = 78.15 t/s, tg_3s = 84.58 t/s
114.31.308.233 I slot print_timing: id 0 | task 972 | n_decoded = 6405, tg = 78.38 t/s, tg_3s = 84.40 t/s
114.34.343.320 I slot print_timing: id 0 | task 972 | n_decoded = 6661, tg = 78.59 t/s, tg_3s = 84.35 t/s
114.37.376.671 I slot print_timing: id 0 | task 972 | n_decoded = 6913, tg = 78.75 t/s, tg_3s = 83.08 t/s
114.40.405.502 I slot print_timing: id 0 | task 972 | n_decoded = 7169, tg = 78.94 t/s, tg_3s = 84.52 t/s
114.43.413.731 I slot print_timing: id 0 | task 972 | n_decoded = 7421, tg = 79.09 t/s, tg_3s = 83.77 t/s
114.46.451.228 I slot print_timing: id 0 | task 972 | n_decoded = 7673, tg = 79.21 t/s, tg_3s = 82.96 t/s
All das mit Qwen3.6-27B. Gemma-4-26B-QAT kann doppelt so schnell laufen, und auch wenn sie vielleicht weniger genau ist als Qwen, fühlt sie sich als Assistent fast sofortig an.
Die absoluten Zahlen sind weniger wichtig als das Verhältnis. Das ist alte, gebrauchte Hardware für etwa $1,100, und diese Änderungen haben die Ausgangsleistung ungefähr verdreifacht.
Lohnt es sich?
Nehmen wir folgende Annahmen:
- Hardwarekosten: $1,100.
- API-Preise: $0.36 für Eingabe und $2.80 für Ausgabe pro Million Tokens für ein gehostetes Qwen 27B auf OpenRouter. Das ist kein ganz fairer Vergleich mit einem quantisierten lokalen Modell, aber irgendeine Annahme braucht man.
- Programmierlast: etwa 15 Eingabetokens pro Ausgabetoken, weil man beim Programmieren mehr liest als schreibt.
- Prompt-Verarbeitung mit 700 Tokens pro Sekunde.
- Ausgabegenerierung mit 60 Tokens pro Sekunde.
Prompt-Verarbeitung und Generierung laufen nacheinander. Für 15 Eingabetokens plus 1 Ausgabetoken beträgt die Zeit 15 / 700 + 1 / 60, etwa 0.0381 Sekunden. Das sind rund 393.75 Eingabe- und 26.25 Ausgabetokens pro Sekunde, also etwa 1.42M Eingabe- und 94.5K Ausgabetokens pro Stunde.
Dieselbe Last über die API zu schicken, würde etwa $0.51 für die Eingabe (1.4175 mal $0.36) und $0.26 für die Ausgabe (0.0945 mal $2.80) kosten. Die Maschine ersetzt also etwa $0.775 API-Kosten pro Stunde aktiver Inferenz, und der Break-even liegt bei $1,100 geteilt durch $0.775, rund 1.420 Stunden.
Wohlgemerkt geht es um Stunden echter Inferenz. Niemand lässt ein Modell acht Stunden am Stück unter Volllast laufen, die Amortisation in Kalenderzeit ist also viel länger. Strom, Wartung, Defekte und Ersatzteile sind gar nicht eingerechnet.
Lohnt es sich also? Diese Rechnung muss jeder selbst machen. Für einen einfachen KI-Assistenten ist die Wirtschaftlichkeit nicht überzeugend, und die Amortisationszeit ist so lang, dass die API schlicht praktischer sein kann. Fügt man aber Agenten-Orchestrierung, unbeaufsichtigte Aufgaben und Pipelines hinzu, kippt das Bild: Lokale Inferenz wirkt plötzlich deutlich vernünftiger. Hier halten halbautonome Pipelines mit seltenen Entscheidungspunkten die Maschine etwa 12 Stunden am Tag beschäftigt, und diese Zahl steigt weiter.