TurboQuant in vLLM on One A100 — Capacity, Speed, and Accuracy of All Four Presets on an 8B Model
vLLM 0.28, Qwen3-8B bf16, one A100 80GB: KV capacity, batched throughput, 32K decode, needle-in-haystack, and GSM8K for bf16, fp8, and all four TurboQuant presets — the 8B size vLLM's own study skipped.

TurboQuant in vLLM on One A100 — Capacity, Speed, and Accuracy of All Four Presets on an 8B Model
vLLM is the only production engine that ships TurboQuant, and vLLM's own study (Part 3) covered 30B–200B models on H100s. That leaves the question most single-GPU users actually have: what do the presets do on one A100, with an 8B model — the setup where you'd be tempted to trade quality for capacity?
We measured all six KV dtypes — bf16, fp8, and the four TurboQuant presets — on Qwen3-8B (bf16 weights, max context 40,960) with vLLM 0.28.0 (+cu129 wheel) on an A100 80GB. Per dtype: KV capacity, batched throughput (64 × 1K→512), single-stream TTFT and decode at 32K context, needle-in-a-haystack at 4K/16K/32K (8 samples each), and GSM8K (200 questions, no-think, greedy).
Three results, one of them a bug report:
- Capacity is real: up to 3.5x.
turboquant_3bit_ncfits 1.37M cache tokens where bf16 fits 396K. - On Ampere, every TurboQuant preset costs about half your throughput. 1,700–2,160 tok/s versus 3,300–3,450 for bf16/fp8, and a third of the decode speed at 32K. The Triton path that is "10–20% overhead" on Hopper is much heavier on sm80.
turboquant_k8v4is broken on A100. GSM8K collapses from 91% to 3.5% and needle recall to 1/24 — not noise, but visibly degenerated output. The other three presets are accurate.
1. Capacity: the Part That Works Exactly as Advertised
| dtype | KV capacity (tokens) | vs bf16 | max concurrent 40K-token requests |
|---|---|---|---|
| bf16 | 396,192 | 1.0x | 9.7 |
| fp8 | 792,400 | 2.0x | 19.3 |
| TQ k8v4 | 877,728 | 2.2x | 21.4 |
| TQ 4bit-nc | 1,152,576 | 2.9x | 28.1 |
| TQ k3v4-nc | 1,253,888 | 3.2x | 30.6 |
| TQ 3bit-nc | 1,374,752 | 3.5x | 33.6 |
These come straight from vLLM's allocator log at gpu_memory_utilization=0.90. If your problem is "how many long-context users fit on this GPU," the presets deliver what the bit math promises.
2. Speed: on Ampere, the Toll Is Heavy
| dtype | batched throughput (tok/s) | vs bf16 | decode @32K, batch 1 | TTFT @32K (s) |
|---|---|---|---|---|
| bf16 | 3,335 | 100% | 71.5 | 3.83 |
| fp8 | 3,445 | 103% | 76.3 | 4.00 |
| TQ k8v4 | 2,158 | 65% | 39.1 | 3.73 |
| TQ 4bit-nc | 1,827 | 55% | 28.1 | 3.94 |
| TQ k3v4-nc | 1,740 | 52% | 25.0 | 3.93 |
| TQ 3bit-nc | 1,702 | 51% | 26.4 | 3.92 |
TTFT barely moves — prefill is compute-bound and the quantized store is cheap. Everything else is expensive: batched throughput halves, and single-stream decode at 32K drops to about a third of bf16. vLLM's study measured 73–80% of baseline throughput on Hopper; on Ampere, where the Triton kernels run through the float8e4b15 fallback and without Hopper's fp8 tensor cores, the same presets land at 51–65%. The PR's own caveat — "Hopper is the primary target" — is worth taking literally.
Note fp8 is faster than bf16 here (+3%) while doubling capacity. On an A100, fp8 isn't a compromise; it's a straight upgrade.
3. Accuracy: Three Presets Fine, One Broken
| dtype | GSM8K (200q) | NIAH 4K/16K/32K (of 8) |
|---|---|---|
| bf16 | 91.0% | 8 / 8 / 8 |
| fp8 | 90.0% | 8 / 8 / 8 |
| TQ k8v4 | 3.5% | 1 / 0 / 0 |
| TQ 4bit-nc | 91.0% | 8 / 8 / 8 |
| TQ k3v4-nc | 87.5% | 7 / 8 / 8 |
| TQ 3bit-nc | 89.5% | 8 / 8 / 8 |
Two things here deserve care.
First, the k8v4 failure is real, and I initially blamed my own harness. When GSM8K came back at 3.5%, my first theory was a broken answer parser or a mangled chat template, so I ran 4bit_nc through the identical harness: 91%. Not the harness. Opening the k8v4 answers settled it — greedy decoding, and this is what came back:
Alright, the first and then the final answer.
Alright, the first and then the final answer.
Alright, the first and then the final answer.
...Short answers still pass, by the way — "What is the capital of France?" gets "Paris." — which is exactly why a smoke test won't catch this. A ~3K-token needle prompt answers with the needle corrupted and the filler leaking: '12345678956789. The quick brown fox jumps over the...'. The k8v4 preset stores keys as fp8 — on Ampere that path uses a Triton float8 fallback (tl.float8e4b15), and the symptoms (fine at short range, degenerate as quantized keys accumulate) point there. vLLM's PR noted early quality failures on tq3 that were fixed by K/V rebalancing; this looks like the same class of defect surviving on sm80. If you are on Ampere, do not use turboquant_k8v4 without running your own eval first. The other presets showed nothing like this, and vLLM's Hopper-based study reported k8v4 as its safest preset — which is exactly why the Ampere check was worth doing.
Second, our 3-bit numbers look better than vLLM's — understand why before celebrating. Their −20-point drops were on AIME25 and LiveCodeBench with long thinking traces on bigger models at up to 256K context; our GSM8K is short-form arithmetic at 4K with no-think decoding, and NIAH is retrieval, which the _nc presets handle well (Part 6 shows why norm correction specifically fixes attention retrieval). Easy tasks tolerate 3-bit keys; hard reasoning does not. Both measurements are right; they bracket the range.
4. So, on an A100, When Would You Use Any of This?
| Situation | Pick |
|---|---|
| Default | fp8 — faster than bf16, 2x capacity, no measurable accuracy cost |
| Memory-bound, short-form or retrieval workload | turboquant_4bit_nc — 2.9x capacity, accuracy held, budget ~half the throughput |
| Memory-bound, reasoning-heavy workload | stay at fp8; vLLM's own study says the 3-bit family costs real points there |
| Any workload on Ampere | avoid `turboquant_k8v4` until the sm80 path is fixed |
The one-sentence version: on Ampere, TurboQuant in vLLM is a capacity tool that charges half your throughput as toll, and one of its four presets is currently broken. On Hopper, trust vLLM's own study instead — the kernels were written for it.
Harness: bench-vllm-tq.py (attached). Each dtype in a fresh process, greedy decoding throughout.
Hardware: 1× A100 80GB, driver 535 / CUDA 12.2. Software: vLLM 0.28.0 (+cu129), Qwen3-8B bf16, max context 40,960.
Wall-clock: ~70 min for all six dtypes. Raw results: vllm-tq.json in the bundle.
Verified on: 2026-08-31.
References
- Part 3: TurboQuant Status Check · Part 5: llama.cpp fork · Part 6: From Scratch
- vLLM, A First Comprehensive Study of TurboQuant · PR #38479
- Zandieh et al., TurboQuant, ICLR 2026
Subscribe to Newsletter
Related Posts

Hybrid Mamba-Transformer, Measured — Qwen3.5-9B Fits 4.4x More Context and 3.6x More Requests on the Same A100
Cache memory of Qwen3.5-9B (24 linear + 8 attention layers) vs Qwen3-8B measured from 2K to 64K context on one A100: 4.4x smaller at 64K, +37% prefill and 3.6x concurrency in vLLM — Part 1's claims confirmed, with a guide to which measurements can legitimately show it.

TurboQuant From Scratch on Real KV Tensors — What 3 Bits Actually Cost, and Why the Forks Beat the Paper's Layout
PolarQuant in 60 lines of PyTorch on real KV from Llama-3.2-1B and Qwen3-8B: 3-bit costs +10% perplexity, k8v4 +0.2%, QJL only pays below 4 bits, and the block-32 layout explains half the forks' edge.

TurboQuant llama.cpp CUDA Fork, Measured on an A100 — turbo4 Matches q4_0, turbo3 Breaks at Long Context
Qwen3-8B Q4_K_M on one A100, six KV types: perplexity, prefill, decode-at-depth, and VRAM measured. turbo4 matches q4_0 quality and beats q8_0 decode 2.5x at depth; turbo3 triples perplexity at 32K context.