Gemma-4 op de DGX Spark: NVFP4 vs BF16
Negen identieke benchmarks, twee precisies. NVFP4 is 22 tot 92 procent sneller per token, en de capaciteit groeit 69 procent op piekuren op de Spark.
In de BF16-baseline van Gemma-4 op de DGX Spark deed ik negen benchmarks met Gemma-4-26B-A4B in BF16. Decode-snelheid hield prima stand, prefill bepaalde wanneer de muur kwam, en het systeem queue’de netjes onder druk in plaats van te crashen. Dat verhaal leek af, totdat NVIDIA een NVFP4-quantized versie van datzelfde model uitbracht.
Zelfde architectuur en fine-tune, zelfde server-config, alleen de precisie verandert. Van BF16 (16 bits per parameter) naar NVFP4 (4 bits per parameter, NVIDIA’s variant op FP4). Vier keer kleiner per gewicht, en als de Blackwell-kernels meewerken ook flink sneller op compute-zware taken.
Op papier dus mooi. In de praktijk: de officiële vLLM v0.20.1-release herkent dit checkpoint zonder gedoe, en de cijfers waren over de hele linie sneller dan de BF16-baseline. Beide tests vallen onder de gids LLMs draaien op de DGX Spark.
Waarom dit überhaupt onderzoeken
Voor een kantoor met een lokale AI-machine is geheugen-budget het meest beperkende ding na rekenkracht. Een 26B model in BF16 neemt ~48 GB GPU-geheugen voor weights alleen. Op een Spark met 128 GB unified memory blijft er zo’n 65 GB over voor KV-cache. Voldoende voor het kantoorscenario uit de eerste blog, maar niet veel ruimte om bijvoorbeeld 30+ gebruikers met grote context naast elkaar te draaien.
NVFP4 reduceert dat tot ~18 GB voor weights. Niet vier keer minder dan BF16 (de vision-encoder blijft BF16, en scale-factors kosten ook ruimte), maar wel ongeveer 2.7× minder. Dat geeft je richting 95 GB KV-cache headroom, wat in theorie veel hogere concurrency moet ondersteunen. Daar komt nog bij dat er minder geheugenverkeer per forward pass nodig is, dus per definitie minder bandwidth-druk, en dat was in BF16 al de bottleneck bij multi-user load. De vraag was dus simpel: hoeveel van die theoretische winst overleeft de praktijk?
Wat NVFP4 eigenlijk is
NVFP4 is NVIDIA’s variant op FP4: floating-point getallen met 4 bits per waarde. Vier bits, niet vier bytes, dus een factor 4 minder per parameter dan BF16. Door per groep gewichten een scaling factor mee op te slaan blijft de accuracy redelijk overeind.
Voor Blackwell ligt het zo. NVIDIA’s datacenter-kaarten (B100, B200, SM10.0) hebben tensor cores die natively met 4-bit waarden kunnen rekenen, en dat is veel sneller dan dezelfde berekening in FP16 of BF16. De DGX Spark daarentegen is desktop-Blackwell (GB10, SM12.1) en die architectuur heeft géén native FP4-compute. Op een datacenter B200 (SM10.0) zou je hier nog 2 tot 3× bovenop verwachten dankzij native FP4 tensor cores. Spark mist die hardware-pad, dus alle winst komt uit geheugen-bandwidth, niet uit compute. Wat je in dat geval krijgt is “weight-only” FP4: de gewichten zijn fysiek 4-bit opgeslagen (vandaar de geheugen-winst), maar tijdens compute worden ze on-the-fly gedecodeerd naar FP16 voor de matrix-multiplications. Een vLLM-warning maakt dat expliciet:
Your GPU does not have native support for FP4 computation but FP4 quantization
is being used. Weight-only FP4 compression will be used leveraging the Marlin kernel.
This may degrade performance for compute-heavy workloads.
Geheugen-winst krijg je dus volledig, compute-winst maar gedeeltelijk. De Marlin INT4 GEMM kernel is geoptimaliseerd, maar niet zo snel als native FP4 op SM10.0 zou zijn. Goed om in te calculeren bij de cijfers verderop.
De testopstelling
Server-config identiek aan de eerste blog, alleen het model wisselt:
docker run -d --name vllm-bench \
--gpus all --ipc=host \
-v appliance_hf-cache:/root/.cache/huggingface \
-p 8000:8000 \
vllm/vllm-openai:v0.20.1 \
--model nvidia/Gemma-4-26B-A4B-NVFP4 \
--served-model-name gemma-4-26b-a4b-nvfp4 \
--max-model-len 131072 \
--gpu-memory-utilization 0.95 \
--kv-cache-dtype fp8 \
--limit-mm-per-prompt '{"image":0,"audio":0}' \
--async-scheduling \
--no-enable-prefix-caching \
--host 0.0.0.0 \
--port 8000
Tests zijn één-op-één gelijk aan de eerste blog: zelfde commands, zelfde concurrency-niveaus, zelfde datasets voor de open-loop tests, zelfde seed. Dat is opzettelijk, want wil je het effect van een geïsoleerde variabele meten (in dit geval de precisie), dan moet alles eromheen gelijk blijven. Hoe ik die concurrency-niveaus, seeds en open-loop-aankomsten precies meet staat in de meetmethode van de Arena.
| Vergelijking | BF16 | NVFP4 |
|---|---|---|
| Model | google/gemma-4-26B-A4B-it | nvidia/Gemma-4-26B-A4B-NVFP4 |
| Active params | 4B | 4B |
| Total params | 26B | 26B |
| Model memory | ~48 GB | ~18 GB |
| KV-cache headroom | ~65 GB | ~95 GB |
| MoE backend | (default) | MARLIN (geforceerd) |
Drie cijfers vatten waar het op uitkomt samen. Klik door voor de volledige run in de Arena, met alle seeds, concurrency-niveaus en commands:
Een interactieve versie van alle cijfers staat op de arena-pagina van Gemma-4-26B-A4B-NVFP4, inclusief commands en TTFT-percentielen voor alle 9 tests.
Run A: context-scaling van 4k naar 25k
Decode per gebruiker bij groeiende context, c=1/5/10:
| Context | Users | BF16 d/u | NVFP4 d/u | Winst |
|---|---|---|---|---|
| 4k | 1 | 24.08 | 29.80 | +24% |
| 4k | 5 | 12.55 | 22.01 | +75% |
| 4k | 10 | 9.48 | 16.94 | +79% |
| 8k | 1 | 23.69 | 29.31 | +24% |
| 8k | 5 | 11.48 | 19.28 | +68% |
| 8k | 10 | 8.52 | 14.35 | +68% |
| 16k | 1 | 23.34 | 28.55 | +22% |
| 16k | 5 | 10.05 | 15.67 | +56% |
| 16k | 10 | 6.79 | 10.06 | +48% |
| 25k | 1 | 22.75 | 27.70 | +22% |
| 25k | 5 | 8.46 | 12.46 | +47% |
| 25k | 10 | 5.40 | 7.55 | +40% |
Bij c=1 is de winst stabiel rond +22-24% door alle contexts heen. Geheugen-bandwidth speelt bij single-user nauwelijks, dus de winst zit hier in het compute-pad zelf. Marlin’s INT4-decode plus FP16-matmul is iets sneller dan BF16’s directe FP16-matmul, ondanks dat het twee stappen zijn.
Bij c=10 schaalt het verschil veel sterker met workload-type, van +40% bij 25k context tot +79% bij 4k. Dat komt doordat bij multi-user de geheugen-bandwidth de bottleneck wordt, en NVFP4 leest minder bytes per forward pass. Hoe meer concurrent, hoe meer dat telt, totdat je weer aan de KV-cache memory limits zit (25k context met meerdere users) en de winst afvlakt.
TTFT (eerste token) is ook beter:
| Context | Users | BF16 TTFT | NVFP4 TTFT |
|---|---|---|---|
| 4k | 10 | 4.46s | 4.20s |
| 8k | 10 | 7.99s | 7.84s |
| 16k | 10 | 18.92s | 18.69s |
| 25k | 10 | 35.67s | 35.65s |
Op TTFT is de winst klein. Dat is logisch: prefill is compute-zwaar, en op SM12.1 zonder native FP4-tensorcores moet Marlin de gewichten on-the-fly decoderen voor de matmul. Dat kost terug wat de geheugen-bandwidth opleverde. Voor decode telt bandwidth zwaarder dan compute, voor prefill andersom.
Run B: 25k context, concurrency tot 20
De stress-test uit deel één:
| Users | BF16 d/u | NVFP4 d/u | BF16 TTFT | NVFP4 TTFT |
|---|---|---|---|---|
| 5 | 8.51 t/s | 12.43 t/s | 19.86s | 19.72s |
| 10 | 5.37 t/s | 7.56 t/s | 35.44s | 35.51s |
| 20 | 3.16 t/s | 4.26 t/s | 67.37s | 67.40s |
Aggregate decode plateau verschuift van 32 t/s naar 36 t/s op c=20: een 12% hoger plafond bij 25k context onder maximale druk. TTFT is praktisch identiek tussen BF16 en NVFP4 omdat prefill hier de muur is en die niet veel sneller wordt op SM12.1. Decode per gebruiker is wel duidelijk beter: bij twintig parallelle 25k-prompts haal je 4.26 in plaats van 3.16 t/s, +35%. Nog steeds geen chat-snelheid, maar wel een merkbaar verschil zodra de tokens beginnen te stromen.
Run C: 1k prompt, 1k output
De korte-prompt + lange-antwoord workload, dichtbij agent-flows en code-generatie:
| Users | BF16 d/u | NVFP4 d/u | Winst |
|---|---|---|---|
| 1 | 23.86 | 29.45 | +23% |
| 5 | 13.59 | 24.69 | +82% |
| 10 | 10.92 | 20.88 | +91% |
Bij c=10 zit per-user decode op ruim 20 t/s, boven leessnelheid en dichtbij comfortabele streaming-UI. Aggregate decode bij c=10 tikt op 209 t/s in plaats van 86 t/s in BF16, bijna een verdubbeling.
Run E: multi-turn (depth 4)
Vijf opeenvolgende beurten per gesprek, tien gesprekken parallel: de meest realistische kantoor-shape.
| Users | BF16 d/u | NVFP4 d/u | BF16 TTFT | NVFP4 TTFT |
|---|---|---|---|---|
| 1 | 23.97 | 29.61 | 0.53s | 0.33s |
| 5 | 13.07 | 23.98 | 1.32s | 1.11s |
| 10 | 10.43 | 19.51 | 2.13s | 1.94s |
Voor tien parallelle 5-turn gesprekken: 1.94 seconden tot eerste token, 19.51 t/s per gebruiker. Dat past comfortabel binnen wat een lezer als chat ervaart, en is 87% sneller per token dan BF16 in dezelfde test.
Run F: RAG-mix (8k prompt)
| Users | BF16 d/u | NVFP4 d/u | BF16 TTFT | NVFP4 TTFT |
|---|---|---|---|---|
| 5 | 12.11 | 20.91 | 4.32s | 4.28s |
| 10 | 9.31 | 15.96 | 7.99s | 8.00s |
| 20 | 6.05 | 10.57 | 14.61s | 14.45s |
8k context is ongeveer wat een RAG-flow met vier chunks van 2k tokens binnenkrijgt. Bij tien gebruikers wacht je 8 seconden tot eerste token (vrijwel gelijk aan BF16, want compute-bottleneck), daarna 16 t/s streamen. Voor “vraag iets over je documenten”-flows ruim werkbaar, en waar de winst zit: in decode-snelheid, niet in TTFT.
Run G: korte instructie, 4096 outputtokens
De agent / code-generatie shape:
| Users | BF16 d/u | NVFP4 d/u | BF16 TTFT | NVFP4 TTFT |
|---|---|---|---|---|
| 1 | 24.17 | 29.59 | 0.24s | 0.11s |
| 5 | 14.32 | 25.79 | 0.38s | 0.23s |
| 10 | 11.75 | 22.54 | 0.48s | 0.37s |
TTFT van 110 milliseconden bij single-user is heel laag, lager dan de meeste hosted APIs over het netwerk halen. En 22.54 t/s per user bij c=10 is ruim genoeg voor agent-streams. Aggregate decode bij c=10 in deze test komt uit op 225 t/s versus 84 t/s in BF16, bijna 2.7× zo veel. Voor een team dat tien gelijktijdige agents draait die elk lange gestructureerde output produceren is dit het belangrijkste cijfer.
Run H: open-loop, random 4k workload
De synthetische kantoor-baseline met Poisson-aankomsten:
| Metric | BF16 | NVFP4 |
|---|---|---|
| Achieved RPS | 0.27 | 0.29 |
| Peak concurrent | 36 | 16 |
| TTFT P50 | 1286 ms | 1006 ms |
| TTFT P99 | 3316 ms | 2893 ms |
| TPOT P50 | 182 ms | 64 ms |
| Total tok/s | 1215 | 1302 |
Wat opvalt is dat de peak concurrent zakt van 36 naar 16 bij identieke arrival rate (0.3 rps) en identieke prompts. Doordat NVFP4 elk verzoek sneller afhandelt blijft de queue korter, en dat is een belangrijk inzicht voor capaciteits-planning: NVFP4 geeft je niet alleen lagere latency per request, maar ook minder queue-druk bij dezelfde arrival rate. Tegelijk zakt TPOT P50 van 182ms naar 64ms. Mediaan inter-token latency dus bijna drie keer sneller. Voor een chat-UI die token-streaming toont is dat het verschil tussen kunstmatig wachten op een antwoord en gewoon meelezen.
Run I: ShareGPT replay (echte gesprekken)
Echte multi-turn conversation data:
| Metric | BF16 | NVFP4 |
|---|---|---|
| Peak concurrent | 17 | 10 |
| TTFT P50 | 353 ms | 152 ms |
| TTFT P99 | 637 ms | 265 ms |
| TPOT P50 | 95 ms | 39 ms |
P99 TTFT van 265 milliseconden, voor 99 procent van de gebruikers. TPOT van 39 ms komt neer op 25.6 t/s per gebruiker. Dat mag je gerust realtime chat noemen voor 25 medewerkers met realistische ShareGPT-stijl prompts.
Run J: Maandagochtend-piek
Het zwaarste scenario uit deel één: overbelaste server, 1.5 rps target met max 25 gelijktijdige requests.
| Metric | BF16 | NVFP4 |
|---|---|---|
| Configured RPS | 1.50 | 1.50 |
| Achieved RPS | 0.26 | 0.44 |
| TTFT P50 | 1132 ms | 920 ms |
| TTFT P99 | 6157 ms | 6054 ms |
| TPOT P50 | 187 ms | 108 ms |
| Total tok/s | 1173 | 1984 |
Het meest meetbare cijfer van de hele dag is dat de achieved RPS van 0.26 naar 0.44 gaat. Hetzelfde target, dezelfde concurrency-cap, dezelfde Poisson-aankomsten, en NVFP4 verwerkt 69% meer verzoeken per seconde voordat de queue dichtslibt.
P99 TTFT verschuift maar marginaal (6.16s naar 6.05s). Dat klopt met het patroon: prefill is compute-bound op SM12.1, en daar is NVFP4 niet veel sneller. Maar TPOT P50 zakt van 187ms naar 108ms, en aggregate token throughput groeit van 1173 naar 1984 t/s. Voor een 25-persoons-kantoor in piekuren is dat het verschil tussen genoeg en knel: meer requests per seconde verwerkt, met snellere streaming voor wie aan de beurt is.
Wat dit betekent voor on-prem AI
Als je een Spark hebt en Gemma-4-26B draait, dan is NVFP4 de upgrade. In alle 9 tests is NVFP4 de winnaar, en het laat 30 GB geheugen vrij voor andere doeleinden zoals meer KV-cache, een tweede klein model ernaast, of batch-jobs. Bij Kamoo staat deze NVFP4-config nu naast de BF16-baseline in bench-spark/, en één commando schakelt tussen de twee.
Voor een 25-persoons-kantoor met realistische ShareGPT-achtige prompts merk je het direct. TPOT P50 zakt van 95 ms naar 39 ms, P99 TTFT van 637 ms naar 265 ms. En als er piekbelasting komt, levert het systeem 69% meer verzoeken per seconde voordat het vol komt te staan. Voor agent-flows en code-generatie (Run G shape) staat de Spark in NVFP4 op zijn sterkst: tien parallelle agents, elk 4096 tokens output, 22.5 t/s per gebruiker met TTFT onder 400 ms.
Voor 25k context-stress (Run B) blijft het de muur. NVFP4 verlaagt ‘m nauwelijks (TTFT verschilt minder dan een seconde), want prefill blijft prefill, en tien parallelle 25k-prompts wachten 35 seconden op het eerste token. Daar verandert quantization niets aan op deze hardware. Wel decode-snelheid: 7.56 t/s/user in plaats van 5.37, dus zodra de tokens komen, lopen ze sneller.
Wat deze run niet zegt
Dit is geen NVFP4 op SM10.0 (datacenter Blackwell). Daar zou native FP4-compute het verschil veel groter maken, met een verwachting van een verdere 2-3× speedup bovenop wat we hier zien. Op een H100 of B200 zijn deze cijfers dus niet representatief; de Spark heeft een specifieke SM12.1-handicap (geen native FP4) die in de cloud niet bestaat.
Dit is ook geen vergelijking met dense Gemma-4-31B in NVFP4. Dense ondergaat een ander code-pad door vLLM’s loader. Voor een vervolg-blog zou dense-NVFP4 met dezelfde testsuite een derde datapunt opleveren.
En dit is geen lange-termijn accuracy-vergelijking. NVFP4 quantization heeft potentieel kleine accuracy-effecten. Voor de typische taken in een kantoor (samenvatting, ticket-classificatie, RAG) zelden merkbaar, voor edge-cases mogelijk wel.
Wat NVIDIA wél heeft gepubliceerd staat in de NVFP4-model-card: op MMLU-Pro, GPQA-Diamond en LiveCodeBench zit NVFP4 binnen 0.2 tot 0.7 punten van hun eigen BF16-baseline. NVIDIA’s eigen BF16-baseline wijkt zelf af van Google’s officiële Gemma-4-card cijfers. Eval-harnesses verschillen meer dan precisie zelf, dus kruisvergelijking tussen vendors zonder identieke harness is wankel. Dat valt binnen run-to-run-variance, geen echte degradatie. Curieus aan diezelfde tabel is dat NVIDIA’s BF16-baseline weer afwijkt van wat Google in de officiële Gemma-4-card publiceert: MMLU-Pro 85.0 vs 82.6, GPQA 80.3 vs 82.3, LiveCodeBench 80.5 vs 77.1. Niet omdat quantization beter wordt dan het origineel, maar omdat eval-harness blijkbaar meer uitmaakt dan de precisie zelf. Andere prompts, andere temperature, andere stop-criteria. Kruisvergelijkingen tussen vendors zijn dus zonder dezelfde harness lastig hard te maken.
Wat blijft hangen
Decode verkoopt de benchmark, prefill bepaalt de ervaring. Dat klopte in deel één en dat klopt nog steeds. Wat NVFP4 toevoegt is dat decode in elke workload sneller wordt, en het meest waar het ertoe doet: bij grotere context en meer gebruikers tegelijk. TTFT blijft op SM12.1 grofweg gelijk omdat prefill compute-bound is en de Spark geen native FP4-tensorcores heeft. Voor wat de gebruiker voelt zodra de tokens beginnen te stromen, is NVFP4 op deze hardware fors beter dan BF16, en het kost niets aan setup-pijn: één officiële vLLM-image, één model-flag, en het draait.