KV-Cache auf 16-GB-GPUs: Lange Kontexte wirklich unterbringen

Warum 128K Kontext auf 16 GB scheitert

Inhaltsverzeichnis

Ein Modell kann einen 128K-Kontextfenster bewerben und trotzdem bei 40K Tokens auf einer 16-GB-GPU scheitern. Die architektonische Obergrenze hat nie versprochen, dass Modellgewichte, KV-Cache, Rechenspeicher und der Desktop-Compositor gleichzeitig auf Ihrer Grafikkarte Platz finden würden.

Der KV-Cache ist in der Regel der Punkt, an dem lange Kontextpläne auf diese physikalische Grenze stoßen. Er wächst mit jedem aktiven Token und jeder Sequenz, sodass eine Konfiguration, die beim Start noch aussichtsreich wirkt, plötzlich stark verlangsamt werden kann, in den Systemspeicher ausweichen muss oder während eines großen Prefills fehlschlägt.

Speicherverbrauch des KV-Caches auf einer 16-GB-GPU

Dieser Leitfaden wandelt das Problem in ein VRAM-Budget um. Er behandelt die Cache-Formel, reproduzierbare Größentabellen für 32K bis 128K und funktionierende Konfigurationen für --cache-type-k und --cache-type-v in llama.cpp, paged und prefix caching in vLLM sowie Kontextsteuerungen in Ollama – inklusive experimenteller adaptive-cache-Forks, die Interesse verdienen, aber keinen blinden Vertrauen. Für den breiteren Kontext rund um Durchsatz, Latenz und Benchmarks, der diesen Zahlen zugrunde liegt, beginnen Sie mit dem LLM-Performance-Hub.

Die kurze Antwort für eine 16-GB-GPU

Beginnen Sie mit einer Sequenz, einem realistischen maximalen Kontext, Flash Attention und einem 8-Bit-KV-Cache. Messen Sie diese Konfiguration, bevor Sie 4-Bit-Cache, CPU-Offload, mehrere parallele Slots oder einen experimentellen Fork versuchen.

Ziel Vernünftiger erster Versuch auf 16 GB Hauptrisiko
32K Q4- oder Q5-Gewichte, Q8 KV, eine Sequenz Modellgewichte lassen zu wenig Pufferplatz
64K Kleinere Modell oder aggressivere Gewichtsquantisierung, Q8 KV Prefill-Latenz und Cache-Bandbreite
128K Kleines GQA-Modell, Q8 oder getesteter Q4 KV, eine Sequenz Cache allein kann die meisten VRAM verbrauchen
Zwei parallele 64K-Sitzungen Als ca. 128K-Cache-Budget betrachten Parallele Kapazität wird mit kostenlosem Durchsatz verwechselt

Meine Meinung ist einfach: Ein stabiles 64K-Setup ist in der Regel nützlicher als ein nominelles 128K-Setup, das am Rande eines Speicherfehlers (Out-of-Memory) läuft. Kontextkapazität ist kein Pokal; sie ist eine Entscheidung in Bezug auf Latenz, Qualität und Parallelität.

Was der KV-Cache speichert

Während der autoregressiven Generierung produziert jede Attention-Ebene Schlüssel- und Wert-Tensoren für jeden verarbeiteten Token. Die Laufzeit behält diese Tensoren bei, damit der nächste Token auf frühere Tokens achten kann, ohne den gesamten Prefix neu berechnen zu müssen.

Der Cache spart enorme Rechenleistung, verbraucht aber Speicher in proportional zum beibehaltenen Token-Zahl. Für einen konventionellen Transformer mit gruppiertem Abfrage-Attention (Grouped-Query Attention) ist eine nützliche Baseline:

KV Bytes = Sequenzen * Tokens * Ebenen * 2 * KV-Köpfe * Kopf-Dimension * Bytes pro Wert

Der Faktor zwei stellt Schlüssel und Werte dar. Multi-Head-Attention verwendet so viele KV-Köpfe wie Abfrageköpfe, Grouped-Query Attention verwendet weniger KV-Köpfe, und Multi-Head-Latent-Attention oder hybride rekurrente Architekturen erfordern andere Berechnungen.

Warum die Parameteranzahl nicht ausreicht

Zwei 8B-Modelle können sehr unterschiedliche KV-Cache-Kosten haben. Eines kann 32 Ebenen und acht KV-Köpfe verwenden, während ein anderes weniger KV-Köpfe, geteilte KV-Ebenen, Sliding-Window-Attention oder komprimierte latente Zustände nutzen kann.

Die Parameteranzahl prognostiziert hauptsächlich den Speicherbedarf der Gewichte. Die KV-Geometrie stammt aus der Attention-Architektur, daher lesen Sie die Modellmetadaten, statt anhand von 8B, 27B oder der GGUF-Dateigröße zu raten. Die klarste Veranschaulichung ist, wie weit sich das Attention-Design über einfache Multi-Head-Attention (MHA) hinaus entwickelt hat:

  • Multi-Query Attention (MQA) teilt einen einzelnen K/V-Kopf über alle Abfrageköpfe – maximale Cache-Ersparnis, aber der radikalste Qualitätskompromiss, der in aktuellen Frontier-Modellen selten allein verwendet wird.
  • Grouped-Query Attention (GQA) gruppiert Abfrageköpfe in Cluster, die jeweils einen K/V-Kopf teilen – der Mainstream-Kompromiss, der von den meisten offenen dichten Modellen verwendet wird und die Geometrie, die die obige Formel annimmt.
  • Multi-Head Latent Attention (MLA), eingeführt in DeepSeek-V2 und in DeepSeek-V3 und Kimi K2 übernommen, geht ganz anders vor: Anstatt K/V über die Köpfe zu teilen, projiziert es Schlüssel und Werte in einen komprimierten, rangniedrigen latenten Vektor und rekonstruiert den K/V in voller Auflösung bei Bedarf zur Attention-Zeit. DeepSeek meldete etwa eine 93%ige KV-Cache-Reduktion im Vergleich zu einem gleichgroßen dichten MHA-Modell, bei gleichzeitig wettbewerbsfähiger – manchmal überlegener – Qualität gegenüber GQA bei gleichem Speicherbudget.

Die praktische Konsequenz ist, dass ein „27B-GQA-Modell“ und ein „27B-MLA-Modell“ KV-Cache-Fußabdrücke haben können, die bei gleicher Kontextlänge um einen Größenordnung auseinanderliegen. Nehmen Sie nicht an, dass die obige Formel auf ein Modell zutrifft, das sich selbst als Nutzung latenter Attention, DeltaNet-Stil-Zustände oder Sliding-Window-Ebenen dokumentiert – prüfen Sie zuerst den Architekturabschnitt der Modellkarte.

Mit Ollama legt ollama show MODEL --verbose Modellmetadaten offen, einschließlich Ebenenzahl, Attention-Köpfe, KV-Köpfe und Kontextlänge, sofern das Format sie bereitstellt. Bei llama.cpp enthält die Ausgabe des Modell-Loaders, die beim Start angezeigt wird, in der Regel äquivalente GGUF-Metadaten und die tatsächliche Cache-Allokation der Laufzeit.

Modell-Limit, allozierter Kontext und genutzter Kontext

Dies sind drei separate Zahlen. Das Modell-Limit ist das Maximum, das durch Training und Positions-Encoding unterstützt wird, der allozierte Kontext ist das, was die Laufzeit reserviert oder zulässt, und der genutzte Kontext sind die aktuell für eine Sequenz beibehaltenen Tokens.

Das Erhöhen einer Engine-Flag kann ein Modell nicht sicher über sein unterstütztes Positionsschema hinaus erweitern. RoPE-Scaling kann einige Architekturen erweitern, aber es ist ein Modellqualitäts-Experiment, keine KV-Speicher-Optimierung.

KV-Cache-Größentabelle: 32K, 64K und 128K Kontextbudgets

Betrachten Sie ein repräsentatives GQA-Modell mit 32 Ebenen, acht KV-Köpfen und einer Kopf-Dimension von 128. Diese Dimensionen produzieren 65.536 Schlüssel- und Wert-Elemente pro Token, bevor mit der Speichergöße jedes Elements multipliziert wird.

Die Tabelle verwendet binäre GiB und die physikalischen Blockgrößen, die gemeinhin mit f16, q8_0 und q4_0 in llama.cpp assoziiert werden. Es ist eine Baseline-Berechnung, kein Versprechen über den Gesamtspeicherbedarf des Prozesses; Ausrichtung, Metadaten, hybride Ebenen und Backend-Workspaces fügen Overhead hinzu.

Cache-Typ Ca. Bytes pro gespeichertem Wert 32K Kontext 64K Kontext 128K Kontext
F16 2.0000 4,00 GiB 8,00 GiB 16,00 GiB
Q8_0 1.0625 2,13 GiB 4,25 GiB 8,50 GiB
Q4_0 0.5625 1,13 GiB 2,25 GiB 4,50 GiB
Q8_0 K plus Q4_0 V Gemischt 1,63 GiB 3,25 GiB 6,50 GiB

Doppeln Sie nun die Ebenenzahl auf 64, während die anderen Dimensionen unverändert bleiben. Der FP16-Cache wird zu 8 GiB bei 32K, 16 GiB bei 64K und 32 GiB bei 128K, was demonstriert, warum eine einzige Kontextempfehlung nicht jedes Modell abdecken kann.

Die tatsächliche 16-GB-Gleichung

Ein praktisches Budget ist breiter als die KV-Formel:

nutzbares VRAM = Gesamt-VRAM - Desktop- und Treiberreserve

KV-Budget = nutzbares VRAM
          - GPU-residenten Modellgewichte
          - Graph- und Aktivierungspuffer
          - Laufzeit-Workspace
          - Zustand der spekulativen Dekodierung
          - Sicherheitsmarge

Auf einer 16-GB-Karte mit angeschlossener Anzeige planen Sie nicht damit, dass alle 16 GiB verfügbar sind. Reservieren Sie mindestens mehrere hundert MiB für Desktop und Treiber, und lassen Sie eine weitere Marge für lastabhängige Puffer; 1,0 bis 1,5 GiB Gesamtatmung ist eine vernünftige Ausgangsannahme, aber Ihre Logs sind die Autorität.

Angenommen, ein GGUF-Modell belegt 10,8 GiB auf der GPU und der Laufzeit-Overhead peak bei etwa 1,2 GiB. Nach einer 1-GiB-Sicherheitsmarge bleiben nur noch etwa 3 GiB für KV übrig, sodass das repräsentative Modell etwa 45K Tokens mit Q8_0 oder 87K mit Q4_0 fasst, vor backend-spezifischem Overhead.

Das macht Q4_0 nicht automatisch zur richtigen Wahl. Wenn die Langzeit-Kontext-Genauigkeit in Ihrer Arbeitslast abnimmt, kann ein kleineres oder aggressiver quantisiertes Modell mit Q8_0-Cache besser sein als größere Gewichte in Kombination mit einem fragilen Cache. Gemessene Anker für genau diese Arithmetik finden Sie in den llama.cpp-Benchmarks für 16 GB VRAM, wo der VRAM-Pro-Modell-Wert bei 19K, 32K und 64K Kontext aufgezeichnet wird. Für einen breiteren Überblick, welche Modellgrößen und Quantisierungsniveaus unter Ollama auf derselben Kartengattung gut funktionieren, siehe LLM-Leistung unter Ollama auf 16GB-VRAM-GPU vergleichen.

Berechnen Sie das KV-Cache-Budget für Ihr Modell

Das folgende Python-Snippet schätzt einen konventionellen Voll-Attention-GQA-Cache. Ersetzen Sie die Geometrie durch Werte aus der Modellkonfiguration oder den GGUF-Metadaten.

def kv_gib(tokens, layers, kv_heads, head_dim, bytes_per_value, sequences=1):
    total = (
        sequences
        * tokens
        * layers
        * 2
        * kv_heads
        * head_dim
        * bytes_per_value
    )
    return total / (1024 ** 3)


model = {
    "layers": 32,
    "kv_heads": 8,
    "head_dim": 128,
}

types = {
    "f16": 2.0,
    "q8_0": 34 / 32,
    "q4_0": 18 / 32,
}

for tokens in (32768, 65536, 131072):
    row = {
        name: round(kv_gib(tokens=tokens, bytes_per_value=size, **model), 2)
        for name, size in types.items()
    }
    print(tokens, row)

Die Q8_0- und Q4_0-Verhältnisse beinhalten einfache Blockmetadaten, weshalb sie etwas größer sind als exakt ein Byte und ein halbes Byte pro Wert. Der Startbericht der Laufzeit bleibt genauer, da er modell-spezifische Cache-Layouts kennt.

Wenn diese Formel falsch ist: Hybrid- und Sliding-Window-Architekturen

Zwingen Sie Hybrid-Architekturen nicht in die konventionelle GQA-Gleichung. Sliding-Window-Ebenen behalten nur ein kürzliches Fenster bei, geteilte KV-Ebenen reduzieren Duplikationen, rekurrente Ebenen können festgrößige Zustände tragen, und Multi-Head-Latent-Attention speichert eine komprimierte Darstellung anstatt von pro-Kopf-K/V-Tensoren – der oben genannte MLA-Fall ist das dramatischste Beispiel.

Moderne Engines verwalten diese gemischten Layouts zunehmend explizit. Verwenden Sie die Formel, um die dominanten Begriffe zu erklären, und bestätigen Sie dann die vom genauen Engine-Build und Backend berichtete Allokation, die Sie bereitstellen möchten.

llama.cpp: Direkte Kontrolle der K- und V-Präzision

llama.cpp bietet getrennte --cache-type-k und --cache-type-v-Optionen in seinem aktuellen Argument-Parser. Dies ist das nützlichste lokale Inferenz-Interface, wenn Sie Cache-Präzision gegen Kontextkapazität tauschen müssen, anstatt ein globales Preset zu akzeptieren. Wenn Sie zuerst das umgebende Installations- und Serving-Setup benötigen, deckt der llama.cpp-Leitfaden llama-cli, llama-server und die wichtigsten VRAM-Flags ab.

Eine konservative 64K-Einbenutzer-Konfiguration sieht so aus:

./llama-server \
  --model /models/model.gguf \
  --n-gpu-layers 999 \
  --ctx-size 65536 \
  --parallel 1 \
  --flash-attn on \
  --cache-type-k q8_0 \
  --cache-type-v q8_0 \
  --batch-size 1024 \
  --ubatch-size 256

Flag-Syntax und Backend-Unterstützung ändern sich schnell, daher führen Sie llama-server --help für den installierten Build aus. Wichtiger ist jedoch, das Start-Log zu inspizieren: Es sollte den beabsichtigten Kontext, die Cache-Typen, das GPU-Offload und die alloziierten K- und V-Puffer anzeigen.

Welche llama.cpp-Cache-Typen auszuprobieren

Beginnen Sie mit Q8_0 für beide K und V. Es halbiert den KV-Speicher im Vergleich zu F16, und unabhängige Perplexitäts-Tests an 20B-und-größeren Modellen (Qwen3.6-27B, Nemotron-30B) zeigen, dass der aggregierte Qualitätsunterschied zu F16 innerhalb des Messrauschens liegt – ein deutlich weniger dramatisches Risiko, als direkt auf Q4_0 umzusteigen, wobei die gleichen Tests zeigten, dass dies bei kleineren Modellen die Dekodiergeschwindigkeit und -genauigkeit bei Langzeitkontexte kollabieren ließ.

Wenn Q8_0 nicht passt, testen Sie Q8_0-Schlüssel mit Q4_0-Werten, bevor Sie beide Seiten auf Q4_0 quantisieren. Diese Reihenfolge hat Forschungsnachhaltigkeit und nicht nur Aberglauben: kontrollierte Bit-Vertilungsstudien an Llama-, Phi-4-, Qwen3- und Mistral-Checkpoints stellten fest, dass Schlüssel-Tensoren konsistent zwei- bis zehnfach empfindlicher gegenüber Quantisierungsfehlern sind als Wert-Tensoren, und dass das Gewähren eines größeren Bit-Budgets für Schlüssel (z. B. 4-Bit-Schlüssel mit 2-Bit-Werten) bis zu 94–98% der Vollpräzisionsgenauigkeit wiederherstellt – während die umgekehrte Aufteilung (2-Bit-Schlüssel, 4-Bit-Werte) bei Aufgaben wie GSM8K 30 Prozentpunkte verlieren kann. Schlüssel bestimmen, auf welche früheren Tokens die Attention tatsächlich trifft, daher ist deren Schutz der architektonisch klügere, nicht nur der sicherer klingende, Schritt.

Konfiguration Speicher Qualitätsrisiko Empfehlung
F16 K und V Höchste Niedrigste Baseline, wenn sie passt
Q8_0 K und V Etwa die Hälfte von F16 Niedrig, aber nicht null Standard 16-GB-Startpunkt
Q8_0 K, Q4_0 V Zwischen Q8 und Q4 Moderat Nützlicher zweiter Schritt
Q4_0 K und V Etwa ein Viertel von F16 Höchste Bei Ziel-Tiefe validieren

Ein Vorbehalt, der verinnerlicht werden sollte: „Niedriges Qualitätsrisiko“ in aggregierten Benchmarks bedeutet nicht null Risiko auf Token-Ebene. Ein kontrollierter Test, der Flash Attention konstant hielt und nur die KV-Präzision unter gieriger (deterministischer) Dekodierung änderte, stellte fest, dass Q8_0-Cache den exakten generierten Text auf den meisten Prompts änderte, und Q4_0 ihn auf praktisch allen änderte – sobald ein Token kippt, kann der Rest der Fortsetzung divergieren. Perplexitäts- und Downstream-Aufgabenscores können im Durchschnitt gut aussehen, während einzelne Outputs sich immer noch vom F16-Baseline unterscheiden. Wenn Ihre Anwendung bytegenaue Reproduzierbarkeit benötigt (Regressionstests, gecachte Antworten, deterministische Agenten), behandeln Sie jede KV-Quantisierung als Verhaltensänderung und nicht nur als Speicheroptimierung, und validieren Sie gegen Ihren eigenen festen Prompt-Satz.

Quantisierte V-Cache können Flash Attention oder einen kompatiblen Backend-Pfad erfordern. Ein Server, der stillschweigend auf einen anderen Typ zurückfällt, invalidiert das Experiment, weshalb Start-Logs wichtiger sind als kopierte Zeilenbefehle.

Kontext, parallele Slots und einheitlicher Cache

--ctx-size beschreibt eine Engine-Kapazität, keine Garantie, dass jeder parallele Slot unabhängig so viele Tokens erhält. Die Cache-Verwaltung hat sich in llama.cpp weiterentwickelt, einschließlich der einheitlichen Cache-Verhalten, daher testen Sie den genauen Build, anstatt sich auf eine ältere Regel zu verlassen, die den Kontext einfach durch die Slot-Anzahl teilt.

Die Kapazitätsgleichung überlebt immer noch Implementierungsänderungen: gleichzeitige eindeutige Tokens brauchen irgendwo Speicher. Wenn zwei Agent-Sitzungen jeweils 48K erreichen können, veranschagen Sie fast 96K lebende Tokens, es sei denn, die Arbeitslast teilt Prefixe oder toleriert Vertreibung und Neuberechnung.

Batch-Größe verkleinert den gespeicherten KV nicht

--batch-size und --ubatch-size betreffen die Prompt-Verarbeitung und temporären Speicher. Das Herabsetzen kann ein großes Prefill vor einem Aktivierungsspeicher-Spike retten, ändert aber nicht die persistenten Bytes, die für jeden beibehaltenen Token erforderlich sind.

Diese Unterscheidung erklärt ein häufiges Fehlmuster: Das Modell startet und eine leere Anfrage funktioniert, aber ein 60K-Prompt schlägt beim Ingestion fehl. Reduzieren Sie den Mikro-Batch, um den transienten Peak zu diagnostizieren; reduzieren Sie Kontext, Cache-Präzision, Parallelität oder Gewichts-Residenz, um die persistente Kapazität zu ändern.

vLLM: Paged-Kapazität ist immer noch Kapazität

vLLM nähert sich dem Problem als Serving-Engine an. Es profiliert verfügbaren Speicher, reserviert einen KV-Cache-Pool und alloziert Cache in Blöcken, damit parallele Sequenzen nicht jede einen großen zusammenhängenden Bereich benötigen. Wenn Sie entscheiden, ob Sie überhaupt zu vLLM wechseln, deckt der Ollama zu vLLM Migration Guide die Arbeitslast-Signale ab; hier ist die Frage rein, wie viel Cache der Pool halten kann, und der vLLM Quickstart deckt Installation und allgemeine Serving-Flags ab, die über die Kapazitätshebel unten hinausgehen.

PagedAttention reduziert Fragmentierung und Verschwendung um variable Sequenzlängen – paged Allokation entfernt Fragmentierung, nicht die pro-Token-Speicherkosten, sodass eine einzige eindeutige 128K-Anfrage immer noch genug Blöcke für ihren KV-Zustand braucht.

Die offizielle vLLM-Speicherschutz-Referenz empfiehlt, max_model_len und max_num_seqs zu begrenzen, wenn Speicher knapp ist, und stellt fest, dass CUDA-Graphen zusätzlichen GPU-Speicher verbrauchen. Auf einer 16-GB-Karte sollten beide Einstellungen absichtlich gewählt und nicht aus der maximalen Konfiguration des Modells übernommen werden.

Ein fokussierter Ein-Sequenz-Server könnte hier beginnen:

vllm serve MODEL_ID \
  --max-model-len 65536 \
  --max-num-seqs 1 \
  --gpu-memory-utilization 0.90 \
  --kv-cache-dtype fp8 \
  --enable-prefix-caching

Nicht jede 16-GB-GPU, jedes Modell, jede Quantisierungsmethode oder jeder Attention-Backend unterstützt diese exakte Kombination. Behandeln Sie es als Konfigurationsform: Beschränken Sie Länge und Parallelität, reservieren Sie Atmung, wählen Sie einen unterstützten Cache-Datatype und validieren Sie den Initialisierungsbericht.

FP8 KV-Cache in vLLM

Die aktuelle vLLM-Quantisierte-KV-Cache-Dokumentation unterstützt FP8-Cache-Formate auf kompatiblen CUDA- und ROCm-Pfaden. FP8 halbiert den rohen Cache-Speicher im Vergleich zu BF16 oder FP16 ungefähr und kann daher die Token-Kapazität oder Parallelität erhöhen.

Skalierung ist wichtig. Die Dokumentation unterscheidet Standard-Skalen, Warm-up-Berechnung und Datensatz-Kalibrierung und empfiehlt die datensatzbasierte Kalibrierung für die höchste Genauigkeit; das einfache Setzen von FP8 mit Skala 1.0 ist bequem, aber nicht automatisch die zuverlässigste Qualitätswahl.

Prefix Caching ist eine Wiederverwendungs-Optimierung

Automatisches Prefix Caching ermöglicht es einer neuen Anfrage, KV-Blöcke für einen identischen gecachten Prefix zu wiederverwenden. Es ist hervorragend für wiederholte Abfragen über dasselbe lange Dokument, geteilte System-Prompts und mehrstufige Konversationen, da es die Neuberechnung des passenden Prefills vermeidet.

Es macht eine eindeutige lange Anfrage nicht kleiner und beschleunigt nicht die Generierung neuer Tokens. Die vLLM-Prefix-Caching-Dokumentation begrenzt den Nutzen explizit auf Prefill-Arbeit mit geteiltem Prefix.

GPU-Speicherausnutzung ist kein freier Speicher

Das Erhöhen von --gpu-memory-utilization gibt vLLM ein größeres Reservationsziel, aber es erzeugt kein VRAM. Das Drängen zu nah an 1.0 kann unzureichenden Raum für die Anzeige, einen anderen Prozess, sich ändernde Aktivierungs-Peaks oder nicht-PyTorch-Allokationen lassen.

Beginnen Sie bei etwa 0.88 bis 0.92 auf einer dedizierten 16-GB-GPU, inspizieren Sie das Profil und erhöhen Sie nur, wenn die Arbeitslast stabil bleibt. Wenn die Initialisierung erfolgreich ist, aber echte Prompts fehlschlagen, reduzieren Sie gebatchte Tokens, Sequenz-Parallelität, CUDA-Graph-Erfassung oder den maximalen Kontext, bevor Sie annehmen, dass der Allokiator kaputt ist.

Ollama: Einfachere Steuerungen, weniger granulare Diagnose

Ollama bietet absichtlich eine kleinere operative Oberfläche. Seine aktuelle Kontextlänge-Dokumentation setzt GPUs unter 24 GiB standardmäßig auf 4K-Kontext, empfiehlt mindestens 64K für Agent- und Coding-Arbeitslasten und warnt, dass größerer Kontext mehr Speicher verbraucht.

Setzen Sie den serverweiten Standard und bestätigen Sie das geladene Modell wie folgt:

OLLAMA_CONTEXT_LENGTH=65536 ollama serve

ollama ps

Sie können auch num_ctx pro Anfrage oder Modell setzen. ollama ps ist wichtig, da seine PROCESSOR- und CONTEXT-Spalten offenbaren, ob das Modell vollständig auf der GPU geblieben ist und ob der angeforderte Kontext tatsächlich alloziert wurde. Beachten Sie, dass das Scheduling-Verhalten hinter diesen Zahlen sich zwischen Ollama-Versionen geändert hat; mein Vergleich der Speicherzuteilung in Ollama v0.12.1 zeigt, dass der neue Scheduler einige Modelle auf einer 16-GB-Karte weiter auf die CPU drängt, daher fixieren Sie die Version, die Sie gemessen haben.

Quantisierter KV-Cache in Ollama

Ollama bietet OLLAMA_KV_CACHE_TYPE mit f16, q8_0 und q4_0-Wahlen in seiner aktuellen FAQ. Quantisierter KV erfordert Flash Attention, was Ollama automatisch auf unterstützten Backends verwendet oder mit OLLAMA_FLASH_ATTENTION=1 angefordert werden kann.

Ein 16-GB-Langkontext-Dienst kann daher gestartet werden als:

OLLAMA_CONTEXT_LENGTH=65536 \
OLLAMA_FLASH_ATTENTION=1 \
OLLAMA_KV_CACHE_TYPE=q8_0 \
OLLAMA_NUM_PARALLEL=1 \
ollama serve

Q8_0 ist die von Ollama empfohlene Alternative zu F16. Die FAQ warnt, dass Q4_0 einen bemerkbareren Qualitätsverlust verursachen kann, besonders bei höherem Kontext, daher sollte es ein gemessenes Fallback sein, kein automatisches 16-GB-Preset.

Ollama-Parallelität multipliziert das Kontext-Budget

Ollama dokumentiert eine besonders klare Regel: Erforderlicher Speicher skaliert mit OLLAMA_NUM_PARALLEL * OLLAMA_CONTEXT_LENGTH. Vier parallele Anfragen bei einer 32K-Einstellung können eine 128K-Aggregat-Kontextallokation für dieses Modell implizieren.

Für einen persönlichen Agenten auf 16 GB halten Sie OLLAMA_NUM_PARALLEL=1, bis eine lange Sitzung stabil ist. Eine zweite Anfrage in die Warteschlange zu stellen, ist in der Regel vorzuziehen, das erste Modell teilweise auf die CPU zu drücken und beide Anfragen zu verlangsamen. Die Mechaniken von Warteschlangen, 503 und Modell-Entladung hinter dieser Wahl sind in wie Ollama parallele Anfragen handhabt dokumentiert.

CPU-Offload: Ein gültiger Ausweg mit einem Preis

Das Verschieben einiger Modell-Ebenen oder KV-Zustände in den System-RAM kann eine Allokationsfehlmeldung in einen funktionierenden Prozess verwandeln. Es bringt aber auch PCIe-Bandbreite und Host-Speicherlatenz in den Dekodierpfad, wo jeder generierte Token den Preis zahlen kann. Die Spuren- und Generierungsnachweise dafür, wann PCIe tatsächlich beißt, finden Sie in LLM-Performance und PCIe-Lanes.

Offload kann für gelegentliche Batch-Arbeit sinnvoll sein, ist aber selten die beste Standardwahl für einen interaktiven Coding-Agenten. Vergleichen Sie zuerst eine kleinere Gewichts-Quantisierung, Q8 KV, reduzierte Parallelität und eine realistische Kontextobergrenze; verwenden Sie Offload, wenn Kapazität wichtiger ist als Latenz.

Achten Sie auf den Abgrund, nicht auf den Durchschnitt. Ein Server kann bei 8K schnell dekodieren, dann nach dem Überlaufen eines Teils des Arbeitssatzes stark verlangsamen, daher Benchmark bei 32K, 64K und der beabsichtigten Maximierung, anstatt nur eine Leerkontext-Tokenrate zu berichten.

Sliding-Window und adaptive KV-Caches

Sliding-Window-Attention ändert das Budget, indem sie nur ein kürzliches Fenster für ausgewählte Ebenen beibehält. Hybrid-Modelle können diese Ebenen mit gelegentlicher globaler Attention oder rekurrentem Zustand kombinieren, wodurch eine flache Vollkontext-Berechnung den Speicher erheblich überschätzt oder fehlplatzieren kann.

Die Optimierung ist Teil der Modellarchitektur, kein generischer Schalter, der ohne Konsequenzen angewendet werden kann. Eine Engine muss das Ebenenmuster, Vertreibungsregeln, Positionen und jegliche globale Tokens korrekt verstehen.

Was adaptive KV-Approaches zu verbessern versuchen

Experimentelle Forks gehen weiter, indem sie Cache-Präzision oder -Layout pro Ebene und Kontexttiefe wählen. Das Ziel ist attraktiv: Höhere Präzision dort beibehalten, wo es wichtig ist, weniger empfindliche Ebenen komprimieren und die Mischung ändern, bevor VRAM-Druck einen harten Überlauf verursacht – dieselbe Schlüssel-Empfindlichkeit gegenüber Wert-Empfindlichkeit, die oben beschrieben ist, ist genau die Art von Signal, die ein adaptiver Allokiator automatisch ausnutzen möchte, anstatt es der manuellen --cache-type-k/--cache-type-v-Justierung zu überlassen.

Ein August 2026 Downstream-Projekt, llama.cpp-adaptive-turboquant, berichtet einen automatischen Selektor für mehrere Ebenen-adaptive Modi und veröffentlicht Langtiefen-Tests auf einer RTX 5080 16 GB. Diese Zahlen sind von Autoren berichtete Ergebnisse von einer spezialisierten Fork, kein Beweis, dass upstream llama.cpp sich gleich verhält.

Warum es immer noch experimentell ist

Die Fork kombiniert benutzerdefinierte Cache-Typen, CUDA-Kerne, modell-spezifische Pfade und Werkzeugketten-Einschränkungen. Das ist weit mehr Code, dem man vertrauen muss, als den Upstream-Cache-Speicher von F16 auf Q8_0 umzuschalten.

Verwenden Sie eine solche Fork nur, wenn Upstream eine echte Anforderung nicht erfüllen kann und Sie Qualität, Stabilität und Geschwindigkeit auf Ihrem Modell reproduzieren können. Notieren Sie den Commit und die CUDA-Version, da ein Ergebnis, das nur an einen Projektnamen hängt, nicht reproduzierbar ist.

Ein fairer adaptive-Cache-Test

Vergleichen Sie die Fork mit einer Upstream-Q8_0-Baseline mit demselben GGUF, Prompt, Sampler, Kontexttiefe und Ausgabelänge. Messen Sie Start-VRAM, Peak-Prefill-VRAM, Prompt-Verarbeitungsgeschwindigkeit, Dekodiergeschwindigkeit und eine Qualitätaufgabe, die tatsächlich Beweise aus dem ältesten Teil des Kontexts erfordert.

Akzeptieren Sie keine erfolgreiche Allokation als vollständiges Ergebnis. Ein Cache kann 128K fassen und trotzdem frühe Fakten verlieren, Ausgaben spät in der Sequenz korrupt machen oder zu langsam dekodieren, um nützlich zu sein.

Ein praktisches 16-GB-Tuning-Verfahren: Eine Variable auf einmal

Der schnellste Weg zu einer stabilen Konfiguration ist, eine Speicherdimension auf einmal zu ändern. Zufälliges Ändern von Cache-Typ, Batch-Größe, Ebenen-Offload, Parallelität und Kontext zusammen erzeugt einen funktionierenden Befehl ohne Erklärung.

flowchart TD A["Schritt 1: Laden bei 8K Kontext, eine Sequenz, Warm-up-VRAM notieren"] --> B{"Gewichte + Laufzeit unter ca. 14.5 GiB?"} B -- "nein" --> C["Wählen Sie eine kleinere Quantisierung oder ein Modell, wiederholen Sie Schritt 1"] C --> A B -- "ja" --> D["Schritt 2: F16/BF16 KV Qualitäts-Baseline, Aufgaben-Ausgaben speichern"] D --> E["Schritt 3: Wechseln zu Q8_0 / FP8 KV mit Flash Attention"] E --> F["Schritt 4: Kontext stufenweise erhöhen - 32K, 64K, 96K, 128K"] F --> G{"Fehler während des Prefills?"} G -- "ja" --> H["Schritt 5: Batch / ubatch-Größe reduzieren"] H --> F G -- "nein" --> I{"Fehler nur bei parallelen Anfragen?"} I -- "ja" --> J["Schritt 5: Parallele Slots / max-num-seqs reduzieren"] J --> F I -- "nein" --> K["Erst jetzt: Gemischte Q8/Q4, volle Q4, Offload, adaptive Fork"]

Schritt 1: Den Gewichts-Boden festlegen

Laden Sie das Modell bei 8K-Kontext, einer Sequenz und dem beabsichtigten GPU-Offload. Notieren Sie die Prozess-VRAM nach dem Warm-up und stellen Sie sicher, dass keine Ebenen unerwartet auf die CPU verschoben wurden.

Wenn die Gewichte und die Laufzeit bereits mehr als etwa 14.5 bis 15 GiB verbrauchen, hat Langzeitkontext keinen gesunden Puffer. Wählen Sie eine kleinere Gewichts-Quantisierung oder ein Modell, bevor Sie den Cache justieren.

Schritt 2: F16 oder BF16 KV als Qualitäts-Baseline messen

Führen Sie den kleinsten Kontext aus, der Ihren Test unterstützt, und behalten Sie den standardmäßigen Hochpräzisions-Cache. Speichern Sie Ausgaben von Retrieval, Code-Editieren, Tool-Auswahl und Langanweisungs-Aufgaben.

Diese Baseline zeigt Ihnen, ob spätere Fehler von der Cache-Quantisierung stammen. Ohne sie kann ein Chat-Template-Problem oder ein schwaches Modell leicht dem Q4 KV angelastet werden.

Schritt 3: Wechsel zu Q8 oder FP8

Aktivieren Sie Flash Attention, wo erforderlich, wählen Sie Q8_0 in llama.cpp oder Ollama, oder einen unterstützten FP8-Modus in vLLM. Wiederholen Sie dieselben Prompts bei denselben Token-Tiefen und stellen Sie sicher, dass das Log den beabsichtigten Cache-Typ zeigt.

Für viele 16-GB-Deployments ist dies der nützliche Stopp-Punkt. Es verdoppelt die rohe KV-Kapazität ungefähr, ohne die Cache-Kompression zur aggressivsten Quantisierung im Stack zu machen.

Schritt 4: Kontext in Stufen erhöhen

Testen Sie 32K, 64K, 96K und 128K, anstatt direkt zum beworbenen Maximum zu springen. Notieren Sie an jeder Stufe Prompt-Verarbeitungstokens pro Sekunde, Dekodier-Tokens pro Sekunde, Peak-VRAM und ob Beweise in der Nähe des Beginns immer noch abgerufen werden können.

Langzeitkontext-Dekodierung verlangsamt sich oft, auch wenn der Speicher passt, weil Attention mehr gecachten Zustand liest. Kapazität und Performance sind separate Achsen.

Schritt 5: Transienten Speicher justieren

Wenn ein Fehler während des Prefills und nicht bei der Initialisierung auftritt, reduzieren Sie Mikro-Batch oder maximal gebatchte Tokens. Wenn ein Fehler nur bei gleichzeitigen Anfragen auftritt, reduzieren Sie Sequenz-Parallelität oder parallele Slots.

Erst wenn diese Steuerungen verstanden sind, sollten Sie gemischte Q8/Q4-Cache, volle Q4-Cache, CPU-Offload oder eine adaptive Fork versuchen. Behalten Sie die Upstream-Q8-Lauf als Vergleichsbasis. Wenn Sie später spekulatives Dekodieren oder MTP hinzufügen, denken Sie daran, dass deren Entwurfspuffer eine weitere Zeile in der Budgetgleichung sind, kein freier Speed – der spekulatives Dekodieren-Leitfaden deckt die Mechaniken und ihre VRAM-Kosten ab, und mein Qwen 3.6 27B und 35B MTP vs Standard Benchmark zeigt genau, wie viel Kontext der zusätzliche Zustand eines MTP-Kopfs auf einer 16-GB-Karte kosten kann.

Was in einem Langzeitkontext-Benchmark aufgezeichnet werden sollte

Eine einzelne tokens/s-Ziffer versteckt genau das Problem, das dieser Artikel zu lösen versucht. Langzeitkontext-Tests sollten genug Detail aufbewahren, damit ein anderer Operator die Speicher-Grenze reproduzieren kann.

Feld Warum es wichtig ist
GPU und nutzbares VRAM Anzeigennutzung und andere Prozesse ändern das Budget
Engine-Version oder Commit Cache-Verhalten und Flags entwickeln sich schnell
Treiber-, CUDA-, ROCm- oder Vulkan-Version Bestimmt Backend- und Kernverhalten
Exaktes Modell und Gewichts-Quantisierung Definiert Gewichts-Residenz und Architektur
K- und V-Cache-Typen Definiert persistente Cache-Größe und Qualitätsrisiko
Kontext-Kapazität und Prompt-Tiefe Allokation ist nicht dasselbe wie tatsächliche Tiefe
Parallele Sequenzen Multipliziert oder teilt Cache-Nachfrage
Batch und Mikro-Batch Beeinflusst Prefill-Peaks und Geschwindigkeit
Prompt-Verarbeitungsgeschwindigkeit Legt Langzeit-Prefill-Nutzbarkeit offen
Dekodiergeschwindigkeit bei jeder Tiefe Legt Cache-Bandbreiten-Verlangsamung offen
Peak-VRAM und CPU-Offload Unterscheidet Passen von Überlaufen
Langzeitkontext-Qualitäts-Ergebnis Erkennt Komprimierungs- oder Positions-Fehler

Verwenden Sie nvidia-smi-Sampling oder das äquivalente Vendor-Tooling während Prefill und Dekodierung. Der Allokationsbericht der Engine ist notwendig, aber das Peak-Gerätespeicher bei einem echten Prompt ist die Zahl, die Stabilität entscheidet.

Häufige KV-Cache-Fehler auf 16-GB-GPUs

128K-Unterstützung als Hardware-Versprechen behandeln

Das Kontext-Feld in einer Modellkonfiguration ist eine architektonische Obergrenze. Sie sagt nichts über den Speicher aus, der nach dem Laden einer bestimmten Quantisierung auf einer bestimmten Engine übrig bleibt.

Berechnen Sie den Cache und validieren Sie die Laufzeit. Marketing-Größen-Kontext ohne VRAM-Budget ist nur ein verzögertes OOM bis zum ersten ernsten Prompt.

Gewichte quantisieren, aber KV vergessen

Eine 4-Bit-GGUF reduziert Modellgewichte, nicht einen F16-KV-Cache. Bei Langzeitkontext kann der Cache die gesamte Ersparnis auslöschen und schließlich den Gewichts-Fußabdruck überschreiten.

Berichten Sie beide Quantisierungen. Q4_K_M Modell, Q8_0 KV ist sinnvoll; 4-Bit-Modell ist unvollständig.

Annehmen, dass Paged Attention Tokens komprimiert

Paging verbessert das Allokations- und Teilungsverhalten. Es ändert nicht die Tensor-Präzision oder entfernt den von einer eindeutigen Sequenz erforderlichen KV-Zustand.

Verwenden Sie paged Allokation, um variable Arbeitslasten effizient zu bedienen. Verwenden Sie Cache-Präzision, Modellarchitektur, Kontext-Obergrenzen und Parallelitätsgrenzen, um die Kapazität zu steuern.

Annehmen, dass Prefix Caching jeden langen Prompt hilft

Prefix Caching spart wiederholte Prefill-Berechnung, wenn Anfragen einen exakten Prefix teilen. Ein einmaliger 100K-Repository-Dump erhält keinen magischen Speicher-Rabatt nur, weil Prefix Caching aktiviert ist.

Es ist eine Arbeitslast-Optimierung, kein Ersatz für die Budgetgleichung. Messen Sie Trefferquote und beibehaltenen Cache-Druck in Multi-User-Serving.

Q4 KV ohne Qualitäts-Test verwenden

Low-Bit-Cache kann subtil fehlschlagen. Das Modell schreibt immer noch flüssigen Text, aber Attention über entfernte Beweise, exakte Namen, Tool-Argumente oder Code-Abhängigkeiten kann degradieren – und wie die oben genannte Token-Divergenz-Forschung zeigt, ist selbst das „sichere“ Q8_0-Setting nicht garantiert, den exakten F16-Output unter deterministischer Dekodierung zu reproduzieren, sondern nur die Genauigkeit im Aggregat zu bewahren.

Testen Sie die Zielaufgabe bei der Ziel-Tiefe. Kurze Chat-Benchmarks sind fast nutzlos für die Validierung eines Langzeitkontext-Caches.

Parallelität auf Auto lassen

Eine Engine kann eine Parallelität wählen, die für den Durchsatz sinnvoll ist, aber für Ihr Langzeitkontext-Ziel unmöglich. Auf 16 GB sind eine tiefe Sequenz und mehrere kurze Sequenzen grundlegend unterschiedliche Arbeitslasten.

Setzen Sie die Grenze explizit, dann erhöhen Sie sie mit gemessenem Traffic. Andernfalls kann eine zweite Anfrage eine stabile 64K-Konfiguration in eine Allokations- oder Latenz-Überraschung verwandeln.

Empfohlene 16-GB-Profile

Diese Profile sind Startpositionen, keine universellen Presets. Ein Modell mit ungewöhnlicher KV-Geometrie – insbesondere ein MLA- oder Hybrid-Sliding-Window-Design – kann viel billiger oder teurer sein als das konventionelle GQA-Beispiel.

Interaktiver Coding-Agent

Verwenden Sie eine Sequenz, 48K bis 64K Kontext, Q8-Cache, Flash Attention und volle GPU-Gewichts-Residenz, wenn möglich. Dieses Profil bevorzugt vorhersehbare Latenz und gute Cache-Präzision gegenüber einer beeindruckenden, aber selten nützlichen Maximierung.

Aktivieren Sie Prefix-Wiederverwendung, wenn die Engine es unterstützt, da Coding-Runden oft einen großen Repository- oder Gesprächs-Prefix teilen. Komprimieren Sie trotzdem Tool-Ausgaben und alte Transkripte; Cache-Engineering macht irrelevante Tokens nicht wertvoll.

Langdokumenten-Analyse

Verwenden Sie ein kleineres Modell mit 64K bis 128K Kapazität, Q8 oder kalibriertem FP8-Cache und wiederholte Prefix-Caching, wenn mehrere Fragen dasselbe Dokument adressieren. Messen Sie die Zeit zum ersten Token, da Prefill dominieren kann, selbst wenn Dekodierung akzeptabel bleibt.

Wenn nur eine Frage gestellt werden soll, kann Retrieval oder stückweise Zusammenfassung schneller und zuverlässiger sein, als den gesamten Korpus durch eine 16-GB-Karte zu drängen. Langzeitkontext ist ein Werkzeug, kein Ersatz für Informationsarchitektur.

Kleiner Multi-User-Server

Begrenzen Sie die Kontextlänge pro Anfrage und die gesamte aktive Sequenzen, anstatt das Modell-Maximum für jeden Client zu beworben. Die paged Allokation von vLLM ist hier nützlich, während Ollama und llama.cpp auch explizite Aufmerksamkeit für Aggregat-Lebende-Tokens erfordern.

Bevorzugen Sie Warteschlagen über unkontrolliertes Überlaufen. Eine langsamere Zulassungspolitik ist weniger schädlich als dass jede Anfrage plötzlich während der Dekodierung über PCIe geht.

Finale Empfehlung für 16-GB-Langzeitkontext

Für Langzeitkontext auf 16 GB sind Q8-KV und eine aktive Sequenz die richtige Baseline. Sie legen die echte Grenze offen, ohne dass Low-Bit-Cache-Qualität, parallele Allokation und Offload-Latenz gleichzeitig fehlschlagen.

Berechnen Sie von der Attention-Geometrie, subtrahieren Sie Gewichte und Laufzeit-Overhead, und bestätigen Sie dann das Ergebnis in Engine-Logs und Peak-Speichermessungen. Wenn 128K immer noch nicht passt, ist ein kleineres Modell oft die sauberste Optimierung; wenn es passt, aber kriecht, ist die Reduzierung des Kontexts oft die ehrliche.

Paged Attention, Prefix Caching, Sliding Windows und adaptive Präzision lösen alle nützliche, aber unterschiedliche Probleme. Das Gewinn-Setup ist das, das auf der GPU bleibt, alte Beweise korrekt abrufen kann und eine akzeptable Dekodiergeschwindigkeit bei der Kontexttiefe aufrechterhält, die Sie tatsächlich verwenden.

Referenzen

Abonnieren

Neue Beiträge zu Systemen, Infrastruktur und KI-Engineering.