On-prem AI··18 min lezen · Bijgewerkt 13 september 2026

Nemotron-3 op de DGX Spark: BF16 vs FP8 vs NVFP4

Nemotron-3 in BF16, FP8 en NVFP4 op dezelfde DGX Spark. Ik vergelijk het geheugengebruik, de uitvoersnelheid en de wachttijd van de langzaamste verzoeken.

Geschreven door Django de Vreng Over de auteur →

In de vorige posts draaide ik Gemma-4 op de DGX Spark. Eerst alleen BF16 als baseline, daarna NVFP4 vs BF16 over dezelfde test-suite. Dat gaf één model in twee precisies. Nuttig, maar nog geen echt beeld van de keuze die je in productie moet maken.

Voor dit stuk draai ik drie varianten van hetzelfde model naast elkaar: BF16, FP8 en NVFP4 van Nemotron-3-Nano-Omni-30B-A3B-Reasoning. Ik gebruik dezelfde Spark, vLLM-versie, prompts en benchmarksuite om de precisies onder vergelijkbare omstandigheden te testen. De afzonderlijke metingen staan als BF16, FP8 en NVFP4 in de arena.

De korte versie: NVFP4 wint op snelheid en throughput, FP8 wint vaker op tail-latency, BF16 is vooral nog nuttig als baseline. De keuze hangt dus af van welke snelheidsmaat voor je toepassing het zwaarst weegt. Onderdeel van de gids LLMs draaien op de DGX Spark.

Waarom dit experiment

De Gemma-post liet vooral zien dat NVFP4 op de Spark werkt. Daarbij liep ik tegen vijf vLLM-bugs aan en gebruikte ik een nightly build met aangepaste flags.

Maar Gemma beantwoordde niet de vraag die ik voor klanten nodig heb: wat kies je als je vandaag een lokaal model op een Spark wil draaien? BF16 omdat dat de originele weights zijn? FP8 omdat Blackwell daar native goed in is? Of NVFP4 omdat je veel meer model en KV-cache in hetzelfde geheugen krijgt?

Daarom vergelijk ik hier drie precisies van hetzelfde model bij chat, RAG-achtige prompts, lange antwoorden en meerdere gelijktijdige gebruikers. Ook test ik piekbelasting.

Wat BF16, FP8 en NVFP4 hier betekenen

BF16 is de baseline: 16 bits per parameter, ongeveer 2 bytes. Voor dit model betekent dat grofweg 61,5 GB aan checkpoint-size. Dat past op de Spark, maar het eet veel van je 128 GB unified memory op voordat er ook maar één gebruiker context in de KV-cache heeft staan.

FP8 halveert dat gewicht ongeveer. De checkpoint is 32,8 GB. Op Blackwell is FP8 een logische keuze: minder geheugen, native ondersteuning, en meestal weinig gedoe in vLLM.

NVFP4 gaat nog verder. De checkpoint is 20,9 GB. Niet vier keer kleiner dan BF16, omdat de vision- en audio-encoders in BF16 blijven, maar klein genoeg om de Spark anders te laten voelen. Meer ruimte voor KV-cache, meer batching, meer concurrency.

De nuance: de DGX Spark draait op desktop Blackwell SM12.1. Daar is NVFP4 niet hetzelfde feest als op datacenter-Blackwell. vLLM gebruikt Marlin om FP4 weights te decoderen richting FP16 tijdens compute. Je krijgt de geheugenwinst volledig. De compute-winst is minder zuiver.

Deze tests laten zien wat de drie precisies op deze machine en met deze software opleveren.

Precisie Model size Geheugen-budget over van 128 GB
BF16 61.5 GB ~66 GB
FP8 32.8 GB ~95 GB
NVFP4 20.9 GB ~107 GB

De testopstelling

Alle runs draaien via Docker op de DGX Spark met vllm/vllm-openai:v0.20.0. Officiële release, geen patches.

docker run -d --name vllm-bench \
  --gpus all --ipc=host \
  -v appliance_hf-cache:/root/.cache/huggingface \
  -p 8000:8000 \
  -e HF_TOKEN="***" \
  vllm/vllm-openai:v0.20.0 \
  vllm serve nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-NVFP4 \
  --max-model-len 131072 \
  --gpu-memory-utilization 0.95 \
  --max-num-seqs 256 \
  --max-num-batched-tokens 8192 \
  --trust-remote-code \
  --video-pruning-rate 0.5 \
  --reasoning-parser nemotron_v3 \
  --enable-auto-tool-choice \
  --tool-call-parser qwen3_coder \
  --limit-mm-per-prompt '{"image":0,"audio":0}'

Voor FP8 gebruik ik hetzelfde profiel met --kv-cache-dtype fp8. BF16 draait zonder die KV-cache-flag. Verder blijft de test gelijk.

De benchmark-suite staat beschreven op de arena-methodologie. Kort gezegd: closed-loop tests voor decode en TTFT per gebruiker, plus open-loop tests met Poisson-aankomsten om te zien hoe de server zich gedraagt als requests niet netjes op elkaar wachten.

Setup

Ik begon verkeerd met nvcr.io/nvidia/vllm:26.02-py3, NVIDIA's eigen vLLM-container. Die had vLLM 0.15.1 en kende de NemotronH_Nano_Omni_Reasoning_V3 architectuur nog niet.

De werkende versie was vllm/vllm-openai:v0.20.0. Officiële release, juiste flashinfer-versies, eerste run werkend.

Onze eigen bench-spark CLI had nog twee kleine fixes nodig: de NVIDIA-entrypoint omzeilen met --entrypoint vllm, en HF_TOKEN automatisch doorgeven aan de container. Daarna liep de suite.

Les: begin met de stable release die de architectuur ondersteunt.

Run A: context-scaling

Deze run is de basis: wat gebeurt er als de prompt langer wordt, terwijl het aantal gebruikers oploopt van één naar tien? Dat raakt direct aan kantoorwerk. Een korte chat is makkelijk. Een RAG-vraag met 25k context en meerdere mensen tegelijk is waar de Spark laat zien hoeveel ruimte er echt over is.

Hier kijk ik naar twee dingen. Eerst decode per gebruiker: hoe snel komt tekst terug zodra de generatie loopt? Daarna TTFT: hoe lang wacht je op het eerste token? Bij lange context is TTFT vaak de pijn die gebruikers als eerste voelen. Ze zien geen tokens, dus het voelt alsof het systeem vastzit.

Single-user is vooral een pure snelheidsmeting. Daar verdubbelt NVFP4 bijna BF16. Bij tien gebruikers wordt het interessanter: de kleinere weights geven vLLM meer ruimte om te batchen, en dan wordt BF16 gewoon zwaar.

Decode/user (tg256), c=1

Context BF16 FP8 NVFP4 NVFP4 vs BF16
4k 29.23 51.68 60.30 +106%
8k 28.59 49.82 55.72 +95%
16k 28.24 47.52 55.24 +96%
25k 28.24 48.85 54.98 +95%

BF16 blijft netjes vlak rond 28-29 tokens per seconde. Dat is stabiel, maar niet snel. FP8 zet daar ongeveer 50 t/s tegenover. NVFP4 zit rond 55-60 t/s. FP8 en NVFP4 genereren in deze test dus duidelijk meer tokens per seconde.

Decode/user (tg256), c=10

Context BF16 FP8 NVFP4 NVFP4 vs BF16
4k 7.76 13.45 19.69 +154%
8k 7.13 11.14 17.90 +151%
16k 6.30 10.73 14.99 +138%
25k 5.56 8.59 12.99 +134%

Bij tien gebruikers is het verschil groter. Op 25k context doet BF16 5,56 tok/s/user. NVFP4 doet 12,99. NVFP4 levert hier dus duidelijk meer antwoordtokens per seconde.

TTFT (eerste token), c=10

Context BF16 FP8 NVFP4
4k 3.90s 2.91s 2.45s
8k 6.49s 5.93s 4.03s
16k 12.63s 10.55s 8.01s
25k 19.82s 16.89s 12.71s

Dit is de tabel die ik voor echte gebruikers het meest serieus neem. Bij 25k context en tien gebruikers wacht je met BF16 bijna 20 seconden op het eerste token. Met NVFP4 is dat 12,7 seconden. Dat verkort de wachttijd, al blijft die voor een chatgesprek lang.

Run B: 25k context, concurrency tot 20

Run A laat zien hoe contextlengte schaalt. Run B houdt de context zwaar en verhoogt alleen de concurrency. Dit is de "iedereen stelt tegelijk een grote vraag"-test.

In de praktijk gebeurt dit niet elk uur. Tien tot twintig mensen klikken zelden exact tegelijk met 25k context op verzenden. Maar als je een lokale AI-machine voor een team neerzet, wil je weten hoe hij faalt. Rustig langzamer worden is acceptabel. Een queue die voelt alsof hij dood is, niet.

NVFP4 laat meer geheugen over voor batching en KV-cache doordat de weights kleiner zijn.

Gebruikers BF16 d/u FP8 d/u NVFP4 d/u NVFP4 vs BF16
5 9.06 15.33 20.75 +129%
10 5.65 9.18 12.99 +130%
20 3.70 5.97 7.79 +110%
Gebruikers BF16 TTFT FP8 TTFT NVFP4 TTFT
5 11.01s 8.89s 7.21s
10 19.75s 15.82s 12.74s
20 37.88s 29.91s 24.08s

Twintig gebruikers met 25k context is expres onaardig. Toch is het nuttig. BF16 zit op 37,88 seconden TTFT. Dat is een lange wachttijd. NVFP4 zit op 24,08 seconden. Dat is nog steeds ruim dertien seconden sneller.

Aggregate decode laat hetzelfde beeld zien:

Gebruikers BF16 FP8 NVFP4
5 34 t/s 53 t/s 71 t/s
10 38 t/s 59 t/s 77 t/s
20 44 t/s 66 t/s 84 t/s

Het plafond verschuift van 44 t/s naar 84 t/s. Voor een enkele gebruiker is dat abstract. Voor een team betekent het dat de queue sneller leegloopt.

Run C: korte prompt, lange output

Dit is de workload voor agents, code-generatie en langere antwoorden: weinig input, veel output. De prompt is maar 1024 tokens, dus prefill is hier niet het probleem. De vraag is vooral hoe snel het model blijft doortikken zodra de output lang wordt.

Daarom kijk ik hier naar decode per gebruiker. TTFT moet laag blijven, maar het echte verschil voel je pas na een paar honderd tokens. Een model dat snel begint maar daarna op 8 tok/s blijft hangen, voelt alsnog traag.

NVFP4 wint hier duidelijk. Bij tien parallelle gebruikers blijft het model op 22,90 tok/s/user zitten. BF16 zakt naar 7,84. Bij lange antwoorden duurt de uitvoer met BF16 daardoor merkbaar langer.

Gebruikers BF16 d/u FP8 d/u NVFP4 d/u
1 28.65 49.85 55.55
5 12.19 21.32 30.97
10 7.84 15.26 22.90

Voor deze workload is NVFP4 de logische default. FP8 is netjes, maar je levert hier vooral snelheid in zonder dat tail-latency de hoofdrol speelt.

Run E: multi-turn, depth 4

Multi-turn is dichter bij echt gebruik dan één losse prompt. Vijf beurten per gesprek, meerdere gesprekken parallel. Dat lijkt op een medewerker die niet één vraag stelt, maar doorvraagt, corrigeert en context meeneemt.

Hier wil ik niet alleen hoge throughput zien. Ik wil vooral dat de server niet elke beurt opnieuw voelt alsof hij uit een koude start komt. Bij tien gesprekken tegelijk wordt dat relevant: de context groeit per gesprek, de scheduler moet blijven delen, en de gebruiker verwacht dat de chat blijft lopen.

Dit is voor mij de belangrijkste kantoor-run. Niet omdat hij perfect echt is, maar omdat hij het dichtst in de buurt komt van "25 mensen gebruiken dit verspreid over de dag".

Gebruikers BF16 d/u FP8 d/u NVFP4 d/u NVFP4 TTFT
1 28.69 49.72 56.18 596 ms
5 11.50 20.87 30.55 1032 ms
10 7.68 14.88 21.58 1359 ms

Bij tien parallelle gesprekken zit NVFP4 op 21,58 tok/s/user. FP8 zit op 14,88. BF16 op 7,68. Dat laatste werkt technisch, maar het voelt niet meer als een vlotte chat. NVFP4 blijft ruim boven de grens waar je antwoord als vloeiend ervaart.

Run F: RAG-mix met 8k prompt

RAG is meestal geen 25k context, maar ook geen korte chat. Deze run gebruikt 8k prompt en 512 outputtokens. Denk aan vier chunks van ongeveer 2k tokens, plus vraag en instructie.

Bij RAG telt prefill meer dan bij Run C. Je stopt elke keer een flinke lap context in het model voordat er iets terugkomt. Daarna wil je genoeg decode overhouden om het antwoord bruikbaar snel te maken.

De vraag is dus: blijft quantization helpen als de prompt zwaarder wordt? Ja. NVFP4 blijft duidelijk voor, ook bij twintig gebruikers.

Gebruikers BF16 d/u FP8 d/u NVFP4 d/u
5 12.50 21.02 27.77
10 8.11 14.37 19.65
20 5.51 9.82 14.09

Bij twintig gebruikers levert NVFP4 14,09 tok/s/user. BF16 zit op 5,51. Voor batch-processing kan dat nog. Voor real-time RAG op een kantoor voelt BF16 krap, zeker als documenten rommelig zijn en prompts langer worden dan je had gehoopt. Dat kan de wachttijd verder verhogen.

Run G: korte instructie, 4096 outputtokens

Run G lijkt op Run C, maar trekt de output veel verder door: 4096 tokens. Dit is de shape van agents die plannen uitschrijven, code genereren, lange analyses maken of meerdere bestanden samenvatten.

Bij dit soort workloads is de eerste token bijna bijzaak. Als het antwoord lang is, bepaalt decode-snelheid de ervaring. Tien seconden verschil aan het begin is vervelend. Minutenlang op output wachten is erger.

NVFP4 blijft hier het sterkst. Belangrijker: het blijft ook bij tien gebruikers boven 25 tok/s/user. Dat is voor lokale hardware op een bureau-machine gewoon bruikbaar.

Gebruikers BF16 d/u FP8 d/u NVFP4 d/u NVFP4 TTFT
1 28.68 49.75 55.44 179 ms
5 14.32 25.56 34.63 427 ms
10 9.51 18.40 25.18 363 ms

BF16 gebruikt in deze vergelijking meer geheugen en heeft meer tijd nodig om dezelfde hoeveelheid output te genereren.

Run H: open-loop kantoor-baseline

Vanaf hier verandert de interpretatie. De vorige runs sturen gecontroleerde batches door het model. Run H gebruikt open-loop traffic: requests komen binnen volgens een Poisson-verdeling. De server moet dus omgaan met aankomsten die niet netjes wachten tot de vorige klaar is.

Dit lijkt meer op een kantoor. Niet perfect, wel beter dan iedereen tegelijk of juist volledig sequentieel. De metrics zijn ook anders. TPOT vertelt hoe snel tokens komen zodra je aan de beurt bent. TTFT P50 vertelt de normale ervaring. TTFT P99 vertelt wat de pechvogel merkt.

Hier wordt FP8 interessant. NVFP4 wint de mediaan en TPOT, maar FP8 wint de tail. Dat is precies waarom ik niet wil eindigen met "NVFP4 is altijd beter".

Metric BF16 FP8 NVFP4
Achieved RPS 0.26 0.28 0.29
Peak concurrent 42 18 15
TTFT P50 1229 ms 732 ms 618 ms
TTFT P99 2996 ms 2008 ms 3235 ms
TPOT P50 203 ms 74 ms 39 ms
Aggregate tok/s 1203 1297 1329

Die peak concurrent van BF16 lijkt op papier goed, maar is het niet. De queue loopt op omdat BF16 hem minder snel leeg krijgt. NVFP4 verwerkt sneller, dus er staan minder requests tegelijk open. Er blijven daardoor minder verzoeken tegelijk openstaan.

De echte keuze zit tussen NVFP4 en FP8. Wil je de beste mediaan en snelste output, dan NVFP4. Wil je de netste P99 op deze workload, dan FP8.

Run I: ShareGPT replay

ShareGPT replay is rommeliger en daardoor nuttig. Echte gesprekken hebben wisselende lengtes, vervolgvragen, korte antwoorden, lange antwoorden en prompts die niet door een benchmark-auteur netjes zijn gladgestreken.

Dit is de run die ik het meest vertrouw voor chatgevoel. Niet voor bedrijfsdocumenten, wel voor de vraag: hoe voelt dit als meerdere mensen door de dag heen gesprekken voeren?

Het patroon uit Run H blijft staan. NVFP4 is het snelst voor de doorsnee gebruiker. FP8 heeft de betere P99.

Metric BF16 FP8 NVFP4
Peak concurrent 17 12 10
TTFT P50 433 ms 220 ms 157 ms
TTFT P99 713 ms 422 ms 1361 ms
TPOT P50 118 ms 38 ms 26 ms

NVFP4 voelt instant voor de meeste gebruikers: 157 ms TTFT P50 en 26 ms TPOT P50. Maar de P99 is 1361 ms, waar FP8 op 422 ms blijft. Dat is een fors verschil.

Voor een interne chat waar een enkele tragere request geen ramp is, kies ik NVFP4. Voor een product-UI met harde latency-belofte zou ik FP8 serieuzer nemen.

Run J: maandagochtend-piek

Run J is oversubscribe. Het target is 1,5 requests per seconde met een concurrency-cap van 25. Dit is niet de normale werkdag. Dit is de test voor wat er gebeurt als de vraag groter is dan de server netjes kan bijhouden.

Bij oversubscribe kijk ik eerst naar achieved RPS. Niet naar configured RPS, want die is voor iedereen hetzelfde. De vraag is hoeveel requests de server daadwerkelijk verwerkt terwijl hij onder druk staat.

Daar wint NVFP4 duidelijk. FP8 houdt de tail netter, maar NVFP4 krijgt veel meer werk door de machine.

Metric BF16 FP8 NVFP4
Configured RPS 1.50 1.50 1.50
Achieved RPS 0.25 0.43 0.58
Peak concurrent 28 28 28
TTFT P50 1130 ms 757 ms 687 ms
TTFT P99 5184 ms 3388 ms 4462 ms
TPOT P50 197 ms 112 ms 82 ms
Aggregate tok/s 1118 1951 2622

Concreet: NVFP4 verwerkt ongeveer 35 requests per minuut. BF16 ongeveer 15. Dat is het verschil tussen een queue die langzaam leegloopt en een queue die gebruikers aan het twijfelen brengt of ze nog een keer moeten klikken. Nogmaals hetzelfde verzoek versturen voegt extra belasting toe.

De drie precisies naast elkaar

Als ik één realistische chat-run moet kiezen, pak ik ShareGPT replay. Daar zie je het onderscheid het schoonst: NVFP4 wint de normale ervaring, FP8 wint de tail, BF16 doet mee maar nergens overtuigend.

Metric BF16 FP8 NVFP4 Beste keuze
TPOT P50 118 ms 38 ms 26 ms NVFP4
TTFT P50 433 ms 220 ms 157 ms NVFP4
TTFT P99 713 ms 422 ms 1361 ms FP8
Peak concurrent 17 12 10 NVFP4
Achieved RPS 0.30 0.30 0.30 gelijk

Bij oversubscribe wordt het verschil harder:

Metric BF16 FP8 NVFP4 Beste keuze
Achieved RPS 0.25 0.43 0.58 NVFP4
TTFT P50 1130 ms 757 ms 687 ms NVFP4
TTFT P99 5184 ms 3388 ms 4462 ms FP8
TPOT P50 197 ms 112 ms 82 ms NVFP4
Aggregate tok/s 1118 1951 2622 NVFP4

Dat maakt de keuze praktischer dan ik vooraf dacht. NVFP4 is de default als je throughput en normale gebruikerservaring wil. FP8 is de keuze als je P99 belangrijker vindt dan mediaan. BF16 is de baseline waarmee je kunt toetsen of quantization de antwoorden verandert.

Waarom FP8 de P99 wint

Mijn hypothese: NVFP4 geeft vLLM meer geheugenruimte en daarmee meer batchingruimte. Dat verhoogt throughput en verlaagt TPOT, maar individuele requests kunnen soms langer wachten voordat ze netjes in een batch vallen.

FP8 heeft minder headroom dan NVFP4, maar nog genoeg voor deze workload. Daardoor lijkt de scheduler voorspelbaarder. Minder agressief, minder snel in mediaan, beter in de tail.

BF16 heeft het slechtste van beide werelden: grote weights, minder KV-cache-headroom en lagere decode. De queue wordt voller, maar niet omdat de server zo veel tegelijk aankan. Hij komt er gewoon minder snel doorheen.

Dit wil ik nog verder uitzoeken met scheduler-instellingen en prefix caching. De ruwe cijfers en de testdefinities staan in de arena zodat ik toekomstige runs naast dezelfde lat kan leggen.

Vergelijking met Gemma-4-26B-A4B

Nemotron-NVFP4 is single-user bijna twee keer sneller dan Gemma-NVFP4. Bij multi-user wordt het verschil kleiner, maar blijft het meestal positief.

Workload Gemma-NVFP4 d/u Nemotron-NVFP4 d/u Ratio
pp4096 c=1 30.01 60.30 2.0×
pp8192 c=1 29.35 55.72 1.9×
pp25000 c=1 28.00 54.98 2.0×
pp4096 c=10 17.05 19.69 1.2×
pp25000 c=10 7.61 12.99 1.7×

Dat patroon klopt bij wat het model is. Nemotron heeft 3B active params, Gemma 4B active params. Bij single-user helpt dat hard. Bij multi-user schuift de bottleneck richting geheugen-bandwidth en scheduling, en dan wordt het verschil kleiner.

Wat dit betekent voor on-prem AI

Mijn default keuze voor deze Spark is NVFP4. Dat baseer ik op de resultaten van deze workloads: hoogste throughput, snelste mediaan, laagste TPOT, kleinste footprint.

Ik kies FP8 wanneer tail-latency belangrijker is dan mediaan. Denk aan een UI waar je wil kunnen zeggen dat 99 procent van de requests binnen een bepaalde grens start. In Run H, I en J wint FP8 consequent op P99 TTFT.

Ik kies BF16 alleen nog als baseline of voor accuracy-kritische validatie. Niet als productie-default. Daarvoor is het op de Spark te duur: ongeveer drie keer zoveel geheugen als NVFP4 en grofweg de helft van de snelheid.

Voor een 25-persoons-kantoor met chat- en RAG-achtige workload zou ik NVFP4 draaien, met een eigen eval-suite ernaast. Voor een externe chatbot met strakke latency-belofte zou ik FP8 testen. Voor BF16 zou ik vooral een korte run bewaren om te zien wat quantization inhoudelijk verandert.

Wat deze runs niet zeggen

Geen accuracy-tests. FP8 en NVFP4 kunnen inhoudelijk afwijken van BF16. Voor productie moet je dat meten op je eigen documenten, je eigen prompts en je eigen fouttolerantie.

Geen multimodal-benchmarks. Nemotron-3-Nano-Omni is multimodal-aware, maar deze runs zijn text-only. Vision en audio blijven hier buiten beeld.

Geen vergelijking met dense modellen. Dit is een MoE-model. Dense modellen voelen anders, vooral bij output-snelheid en hoe vLLM ermee omgaat.

Geen definitieve scheduler-conclusie. De FP8-vs-NVFP4-tail is interessant genoeg om apart te testen met andere batching- en scheduling-instellingen.

Waar ik land

De precisie beïnvloedt hoeveel geheugen overblijft en hoe snel het model reageert. Op de Spark maken die verschillen uit voor het aantal gebruikers dat je tegelijk kunt bedienen.

NVFP4 levert in veel runs ongeveer twee keer de decode-snelheid van BF16. FP8 heeft in de open-looptests een gunstigere P99 TTFT. BF16 blijft mijn referentie voor het beoordelen van veranderingen door quantization.

De praktische les uit deze drie posts samen: volg de vendor recipes, draai de stable image en meet je eigen workload. Ik begin voortaan met de ondersteunde configuratie en pas die aan wanneer de metingen daar aanleiding toe geven.