---
title: "Nemotron-3 op de DGX Spark: BF16 vs FP8 vs NVFP4"
description: "Eén model, drie precisies, dezelfde Spark. Wat geheugen-budget, decode-snelheid en tail-latency doen wanneer je van 16 bit naar 8 bit naar 4 bit gaat."
canonical: "https://djangodevreng.nl/blog/nemotron-3-dgx-spark-precisies/"
pubDate: "2026-05-03T00:00:00.000Z"
updatedDate: "2026-05-05T00:00:00.000Z"
category: "on-prem"
---
In de vorige posts draaide ik Gemma-4 op de DGX Spark. Eerst [alleen BF16 als baseline](/blog/gemma-4-dgx-spark-benchmarks/), daarna [NVFP4 vs BF16 over dezelfde test-suite](/blog/gemma-4-nvfp4-vs-bf16-dgx-spark/). 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. Zelfde Spark. Zelfde vLLM-versie. Zelfde prompts. Zelfde benchmark-suite. Zo dicht bij een eerlijke quantization-vergelijking als ik hem op deze machine kan krijgen.

De korte versie: **NVFP4 wint op snelheid en throughput, FP8 wint vaker op tail-latency, BF16 is vooral nog nuttig als baseline**. Dat is minder netjes dan "4 bit is altijd beter". Gelukkig maar, anders was deze post kort geweest. Onderdeel van de gids [LLMs draaien op de DGX Spark](/dgx-spark/).

## Waarom dit experiment

De Gemma-post liet vooral zien dat NVFP4 op de Spark werkt. Wel met pijn. Vijf vLLM-bugs, een nightly build en genoeg flags om een command-regel eruit te laten zien als een kleine bekentenis.

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 deze run. Eén model in drie precisies. Geen leaderboard-score, maar workloads die lijken op kantoorwerk: chat, RAG, langere antwoorden, meerdere gebruikers tegelijk en een maandagochtend waarop iedereen ineens denkt dat AI toch handig is.

## 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.

Voor deze post maakt dat juist interessant. Dit is geen theoretische quantization-post. Dit is: wat gebeurt er op deze machine, met deze stack, als je de drie opties echt draait?

| 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.

```bash
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](/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 oplossing was saaier: `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.

<details>
<summary>Run A: context-scaling</summary>

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. Voor één gebruiker is dat het verschil tussen "prima" en "dit voelt lokaal maar niet lokaal-traag".

### 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 NVFP4 niet "wat sneller". Het is een andere klasse. Op 25k context doet BF16 5,56 tok/s/user. NVFP4 doet 12,99. Dat is nog steeds geen cloud-GPU-cluster, maar het verschil in gevoel is groot: BF16 wordt wachten, NVFP4 blijft werken.

### 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. Nog steeds lang, maar niet hetzelfde soort lang.

</details>

<details>
<summary>Run B: 25k context, concurrency tot 20</summary>

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 houdt hier de meeste lucht. Niet omdat het model slimmer wordt, maar omdat de server met kleinere weights meer ruimte heeft voor batching en KV-cache.

| 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 voelt stuk. NVFP4 zit op 24,08 seconden. Ook niet gezellig, maar 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.

</details>

<details>
<summary>Run C: korte prompt, lange output</summary>

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. Dat is nog leesbaar, maar voor een agent-flow voelt het alsof iemand met de hand meetypt.

| 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.

</details>

<details>
<summary>Run E: multi-turn, depth 4</summary>

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.

</details>

<details>
<summary>Run F: RAG-mix met 8k prompt</summary>

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 worden ze altijd.

</details>

<details>
<summary>Run G: korte instructie, 4096 outputtokens</summary>

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 |

Voor agent-flows is dit vrij hard: BF16 is niet stuk, maar je betaalt elke lange output dubbel. Eerst in geheugen, daarna in wachttijd.

</details>

<details>
<summary>Run H: open-loop kantoor-baseline</summary>

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. Dat is geen lagere capaciteit, dat is minder file.

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.

</details>

<details>
<summary>Run I: ShareGPT replay</summary>

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.

</details>

<details>
<summary>Run J: maandagochtend-piek</summary>

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. Niet klikken. Nooit helpen die tweede klikken.

</details>

## 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 checkt of quantization je accuracy sloopt.

## 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](/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**. Niet omdat 4 bit principieel mooier is, maar omdat de cijfers bij deze workloads het dragen: 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-keuze is geen detail. Op de Spark bepaalt hij of dezelfde machine voelt als een lokaal experiment of als iets dat je aan collega's kunt geven zonder elke vijf minuten uitleg te moeten geven.

NVFP4 verdubbelt in veel runs de bruikbare ervaring ten opzichte van BF16. FP8 is minder spectaculair, maar voorspelbaarder in de tail. BF16 blijft nuttig als referentiepunt, niet als eindstation.

De praktische les uit deze drie posts samen: volg de vendor recipes, draai de stable image en meet je eigen workload. Niet zelf knutselen tenzij je daar een goede reden voor hebt. Ik had bij Gemma een reden. Achteraf was hij middelmatig.