Nemotron-3 sur le DGX Spark : BF16 vs FP8 vs NVFP4
Un modèle, trois précisions, le même Spark. Ce que font budget mémoire, vitesse de decode et tail-latency quand tu passes de 16 à 8 puis 4 bits.
Dans les posts précédents, j’ai fait tourner Gemma-4 sur le DGX Spark. D’abord juste BF16 comme baseline, puis NVFP4 vs BF16 sur la même suite de tests. Ça donnait un modèle dans deux précisions. Utile, mais pas encore une vraie image du choix que tu dois faire en production.
Pour cet article, je fais tourner trois variantes du même modèle côte à côte : BF16, FP8 et NVFP4 de Nemotron-3-Nano-Omni-30B-A3B-Reasoning. Même Spark. Même version de vLLM. Mêmes prompts. Même suite de benchmarks. Aussi proche d’une comparaison de quantization honnête que je peux l’obtenir sur cette machine.
La version courte : NVFP4 gagne sur la vitesse et le throughput, FP8 gagne plus souvent sur la tail-latency, BF16 reste surtout utile comme baseline. C’est moins net que “4 bits c’est toujours mieux”. Heureusement, sinon ce post aurait été court. Fait partie du guide faire tourner des LLMs sur le DGX Spark.
Pourquoi cette expérience
Le post sur Gemma montrait surtout que NVFP4 fonctionne sur le Spark. Avec de la douleur. Cinq bugs vLLM, une nightly build et assez de flags pour qu’une ligne de commande ressemble à un petit aveu.
Mais Gemma ne répondait pas à la question dont j’ai besoin pour les clients : que choisis-tu si tu veux faire tourner un modèle local sur un Spark aujourd’hui ? BF16 parce que ce sont les weights originaux ? FP8 parce que Blackwell y est nativement bon ? Ou NVFP4 parce que tu fais tenir beaucoup plus de modèle et de KV-cache dans la même mémoire ?
D’où cette run. Un modèle dans trois précisions. Pas un score de leaderboard, mais des workloads qui ressemblent au travail de bureau : chat, RAG, réponses plus longues, plusieurs utilisateurs en même temps, et un lundi matin où tout le monde décide soudain que l’IA est bien pratique finalement.
Ce que BF16, FP8 et NVFP4 signifient ici
BF16 est la baseline : 16 bits par paramètre, environ 2 octets. Pour ce modèle ça veut dire à peu près 61,5 GB de checkpoint size. Ça tient sur le Spark, mais ça grignote une grande partie de tes 128 GB de mémoire unifiée avant qu’un seul utilisateur n’ait du contexte dans le KV-cache.
FP8 divise ce poids à peu près par deux. Le checkpoint fait 32,8 GB. Sur Blackwell, FP8 est un choix logique : moins de mémoire, support natif, et en général peu d’embêtements dans vLLM.
NVFP4 va plus loin. Le checkpoint fait 20,9 GB. Pas quatre fois plus petit que BF16, parce que les encoders vision et audio restent en BF16, mais assez petit pour rendre le Spark différent. Plus de place pour le KV-cache, plus de batching, plus de concurrency.
La nuance : le DGX Spark tourne sur du desktop Blackwell SM12.1. Là, NVFP4 n’est pas la même fête que sur du datacenter Blackwell. vLLM utilise Marlin pour décoder les weights FP4 vers FP16 pendant le compute. Tu obtiens le gain mémoire en entier. Le gain compute est moins pur.
Pour ce post, c’est justement ce qui le rend intéressant. Ce n’est pas un post théorique sur la quantization. C’est : que se passe-t-il sur cette machine, avec cette stack, quand tu fais vraiment tourner les trois options ?
| Précision | Model size | Budget mémoire restant sur 128 GB |
|---|---|---|
| BF16 | 61.5 GB | ~66 GB |
| FP8 | 32.8 GB | ~95 GB |
| NVFP4 | 20.9 GB | ~107 GB |
Le banc de test
Toutes les runs passent par Docker sur le DGX Spark avec vllm/vllm-openai:v0.20.0. Release officielle, pas de 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}'
Pour FP8, j’utilise le même profil avec --kv-cache-dtype fp8. BF16 tourne sans ce flag KV-cache. Le reste du test reste identique.
La suite de benchmarks est décrite sur la méthodologie de l’arena. En bref : tests closed-loop pour le decode et le TTFT par utilisateur, plus des tests open-loop avec des arrivées de Poisson pour voir comment le serveur se comporte quand les requests n’attendent pas gentiment l’une l’autre.
Setup
J’ai mal commencé avec nvcr.io/nvidia/vllm:26.02-py3, le container vLLM de NVIDIA. Il avait vLLM 0.15.1 et ne connaissait pas encore l’architecture NemotronH_Nano_Omni_Reasoning_V3.
La solution était plus ennuyeuse : vllm/vllm-openai:v0.20.0. Release officielle, bonnes versions de flashinfer, première run fonctionnelle.
Notre propre CLI bench-spark avait encore besoin de deux petits fixes : contourner l’entrypoint NVIDIA avec --entrypoint vllm, et passer HF_TOKEN automatiquement au container. Après ça, la suite a tourné.
Leçon : commence par la release stable qui supporte l’architecture.
Run A : context-scaling
Cette run est la base : que se passe-t-il quand le prompt devient plus long, pendant que le nombre d’utilisateurs grimpe de un à dix ? Ça touche directement au travail de bureau. Un chat court est facile. Une question RAG avec 25k de contexte et plusieurs personnes en même temps, c’est là que le Spark montre combien de place il reste vraiment.
Ici, je regarde deux choses. D’abord le decode par utilisateur : à quelle vitesse le texte revient une fois que la génération tourne ? Ensuite le TTFT : combien de temps attends-tu le premier token ? Avec un long contexte, le TTFT est souvent la douleur que les utilisateurs ressentent en premier. Ils ne voient aucun token, donc on a l’impression que le système est bloqué.
Single-user est surtout une mesure de vitesse pure. Là, NVFP4 double presque BF16. À dix utilisateurs, ça devient plus intéressant : les weights plus petits donnent à vLLM plus de place pour batcher, et là BF16 devient simplement lourd.
Decode/user (tg256), c=1
| Contexte | 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 reste bien plat autour de 28-29 tokens par seconde. C’est stable, mais pas rapide. FP8 met environ 50 t/s en face. NVFP4 se situe autour de 55-60 t/s. Pour un seul utilisateur, c’est la différence entre “correct” et “ça fait local mais pas local-lent”.
Decode/user (tg256), c=10
| Contexte | 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% |
À dix utilisateurs, NVFP4 n’est pas “un peu plus rapide”. C’est une autre classe. À 25k de contexte, BF16 fait 5,56 tok/s/user. NVFP4 fait 12,99. Ce n’est toujours pas un cluster de GPU cloud, mais la différence de ressenti est grande : BF16 devient de l’attente, NVFP4 continue de travailler.
TTFT (premier token), c=10
| Contexte | 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 |
C’est le tableau que je prends le plus au sérieux pour de vrais utilisateurs. À 25k de contexte et dix utilisateurs, tu attends presque 20 secondes le premier token avec BF16. Avec NVFP4 c’est 12,7 secondes. Toujours long, mais pas le même genre de long.
Run B : 25k de contexte, concurrency jusqu'à 20
La Run A montre comment la longueur de contexte scale. La Run B garde le contexte lourd et augmente seulement la concurrency. C’est le test “tout le monde pose une grosse question en même temps”.
En pratique, ça n’arrive pas toutes les heures. Dix à vingt personnes cliquent rarement sur envoyer exactement au même moment avec 25k de contexte. Mais si tu poses une machine IA locale devant une équipe, tu veux savoir comment elle échoue. Ralentir calmement est acceptable. Une queue qui a l’air morte, non.
NVFP4 garde le plus d’air ici. Pas parce que le modèle devient plus malin, mais parce que le serveur avec des weights plus petits a plus de place pour le batching et le KV-cache.
| Utilisateurs | 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% |
| Utilisateurs | 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 |
Vingt utilisateurs avec 25k de contexte, c’est exprès méchant. Pourtant c’est utile. BF16 est à 37,88 secondes de TTFT. Ça fait cassé. NVFP4 est à 24,08 secondes. Pas confortable non plus, mais quand même bien treize secondes plus rapide.
L’aggregate decode montre la même image :
| Utilisateurs | 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 |
Le plafond passe de 44 t/s à 84 t/s. Pour un seul utilisateur, c’est abstrait. Pour une équipe, ça veut dire que la queue se vide plus vite.
Run C : prompt court, output longue
C’est le workload pour les agents, la génération de code et les réponses plus longues : peu d’input, beaucoup d’output. Le prompt ne fait que 1024 tokens, donc le prefill n’est pas le problème ici. La question est surtout : à quelle vitesse le modèle continue de tourner une fois que l’output devient longue ?
Donc ici, je regarde le decode par utilisateur. Le TTFT doit rester bas, mais la vraie différence, tu ne la sens qu’après quelques centaines de tokens. Un modèle qui démarre vite mais reste ensuite coincé à 8 tok/s donne quand même une impression de lenteur.
NVFP4 gagne clairement ici. À dix utilisateurs en parallèle, le modèle reste à 22,90 tok/s/user. BF16 tombe à 7,84. C’est encore lisible, mais pour un flow d’agent, on dirait que quelqu’un tape à la main en même temps.
| Utilisateurs | 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 |
Pour ce workload, NVFP4 est le default logique. FP8 est correct, mais ici tu cèdes surtout de la vitesse sans que la tail-latency joue le rôle principal.
Run E : multi-turn, depth 4
Le multi-turn est plus proche de l’usage réel qu’un prompt isolé. Cinq tours par conversation, plusieurs conversations en parallèle. Ça ressemble à un employé qui ne pose pas une question, mais relance, corrige et garde le contexte.
Ici, je ne veux pas seulement voir du throughput élevé. Je veux surtout que le serveur ne donne pas l’impression de sortir d’un cold start à chaque tour. Avec dix conversations en même temps, ça devient pertinent : le contexte grandit par conversation, le scheduler doit continuer à partager, et l’utilisateur s’attend à ce que le chat continue de tourner.
C’est pour moi la run de bureau la plus importante. Pas parce qu’elle est parfaitement réelle, mais parce qu’elle se rapproche le plus de “25 personnes utilisent ça réparties sur la journée”.
| Utilisateurs | 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 |
À dix conversations en parallèle, NVFP4 est à 21,58 tok/s/user. FP8 est à 14,88. BF16 à 7,68. Ce dernier fonctionne techniquement, mais ça ne donne plus l’impression d’un chat fluide. NVFP4 reste bien au-dessus de la ligne où tu perçois une réponse comme fluide.
Run F : mix RAG avec prompt de 8k
Le RAG n’est en général pas 25k de contexte, mais pas non plus un chat court. Cette run utilise un prompt de 8k et 512 tokens d’output. Pense à quatre chunks d’environ 2k tokens, plus la question et l’instruction.
Avec le RAG, le prefill compte plus que dans la Run C. Tu enfournes à chaque fois une bonne tranche de contexte dans le modèle avant que quelque chose revienne. Ensuite, tu veux garder assez de decode pour rendre la réponse utilement rapide.
La question est donc : la quantization continue-t-elle d’aider quand le prompt s’alourdit ? Oui. NVFP4 reste clairement devant, même à vingt utilisateurs.
| Utilisateurs | 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 |
À vingt utilisateurs, NVFP4 délivre 14,09 tok/s/user. BF16 est à 5,51. Pour du batch processing, ça peut encore aller. Pour du RAG temps réel dans un bureau, BF16 fait juste, surtout quand les documents sont en désordre et que les prompts deviennent plus longs que tu l’espérais. Ils le deviennent toujours.
Run G : instruction courte, 4096 tokens d'output
La Run G ressemble à la Run C, mais pousse l’output bien plus loin : 4096 tokens. C’est la shape des agents qui rédigent des plans, génèrent du code, font de longues analyses ou résument plusieurs fichiers.
Pour ce genre de workload, le premier token est presque secondaire. Si la réponse est longue, la vitesse de decode détermine l’expérience. Dix secondes de différence au début, c’est agaçant. Attendre l’output pendant des minutes, c’est pire.
NVFP4 reste le plus fort ici. Plus important : il reste aussi au-dessus de 25 tok/s/user à dix utilisateurs. Pour du hardware local sur une machine de bureau, c’est tout simplement utilisable.
| Utilisateurs | 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 |
Pour les flows d’agents, c’est assez tranché : BF16 n’est pas cassé, mais tu paies chaque output longue deux fois. D’abord en mémoire, ensuite en temps d’attente.
Run H : baseline bureau open-loop
À partir d’ici, l’interprétation change. Les runs précédentes poussent des batches contrôlés à travers le modèle. La Run H utilise du trafic open-loop : les requests arrivent selon une distribution de Poisson. Le serveur doit donc gérer des arrivées qui n’attendent pas gentiment que la précédente soit finie.
Ça ressemble plus à un bureau. Pas parfait, mais mieux que tout le monde en même temps ou tout à fait séquentiel. Les metrics sont différentes aussi. Le TPOT dit à quelle vitesse les tokens arrivent une fois que c’est ton tour. Le TTFT P50 dit l’expérience normale. Le TTFT P99 dit ce que remarque le malchanceux.
Ici, FP8 devient intéressant. NVFP4 gagne la médiane et le TPOT, mais FP8 gagne la tail. C’est exactement pour ça que je ne veux pas finir avec “NVFP4 est toujours mieux”.
| 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 |
Ce peak concurrent de BF16 a l’air bon sur le papier, mais ne l’est pas. La queue monte parce que BF16 la vide moins vite. NVFP4 traite plus vite, donc moins de requests sont ouvertes en même temps. Ce n’est pas une capacité plus faible, c’est moins de file d’attente.
Le vrai choix est entre NVFP4 et FP8. Tu veux la meilleure médiane et l’output la plus rapide, alors NVFP4. Tu veux le P99 le plus propre sur ce workload, alors FP8.
Run I : replay ShareGPT
Le replay ShareGPT est plus brouillon et donc utile. Les vraies conversations ont des longueurs variables, des questions de suivi, des réponses courtes, des réponses longues et des prompts qui n’ont pas été gentiment lissés par un auteur de benchmark.
C’est la run en laquelle j’ai le plus confiance pour le ressenti chat. Pas pour des documents d’entreprise, mais pour la question : qu’est-ce que ça donne quand plusieurs personnes mènent des conversations tout au long de la journée ?
Le pattern de la Run H tient. NVFP4 est le plus rapide pour l’utilisateur moyen. FP8 a le meilleur 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 donne une impression instantanée pour la plupart des utilisateurs : 157 ms de TTFT P50 et 26 ms de TPOT P50. Mais le P99 est de 1361 ms, là où FP8 reste à 422 ms. C’est une grosse différence.
Pour un chat interne où une seule request plus lente n’est pas un drame, je choisis NVFP4. Pour une UI produit avec une promesse de latence dure, je prendrais FP8 plus au sérieux.
Run J : pic du lundi matin
La Run J est en oversubscribe. La cible est de 1,5 requests par seconde avec une concurrency-cap de 25. Ce n’est pas la journée de travail normale. C’est le test de ce qui se passe quand la demande est plus grande que ce que le serveur peut suivre proprement.
En oversubscribe, je regarde d’abord l’achieved RPS. Pas le configured RPS, parce qu’il est le même pour tout le monde. La question est : combien de requests le serveur traite réellement pendant qu’il est sous pression ?
Là, NVFP4 gagne clairement. FP8 garde la tail plus propre, mais NVFP4 fait passer beaucoup plus de travail par la 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 |
Concrètement : NVFP4 traite environ 35 requests par minute. BF16 environ 15. C’est la différence entre une queue qui se vide lentement et une queue qui fait douter les utilisateurs s’ils doivent cliquer encore une fois. Ne clique pas. Ce deuxième clic n’aide jamais.
Les trois précisions côte à côte
Si je dois choisir une run de chat réaliste, je prends le replay ShareGPT. C’est là que tu vois la distinction le plus proprement : NVFP4 gagne l’expérience normale, FP8 gagne la tail, BF16 participe mais ne convainc nulle part.
| Metric | BF16 | FP8 | NVFP4 | Meilleur choix |
|---|---|---|---|---|
| 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 | égalité |
En oversubscribe, la différence se durcit :
| Metric | BF16 | FP8 | NVFP4 | Meilleur choix |
|---|---|---|---|---|
| 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 |
Ça rend le choix plus pratique que je ne le pensais au départ. NVFP4 est le default si tu veux du throughput et une expérience utilisateur normale. FP8 est le choix si tu trouves le P99 plus important que la médiane. BF16 est la baseline qui te sert à vérifier si la quantization démolit ton accuracy.
Pourquoi FP8 gagne le P99
Mon hypothèse : NVFP4 donne à vLLM plus de place mémoire et donc plus de place pour le batching. Ça augmente le throughput et baisse le TPOT, mais des requests individuelles peuvent parfois attendre plus longtemps avant de tomber proprement dans un batch.
FP8 a moins de headroom que NVFP4, mais encore assez pour ce workload. Du coup, le scheduler semble plus prévisible. Moins agressif, moins rapide en médiane, meilleur dans la tail.
BF16 a le pire des deux mondes : gros weights, moins de headroom KV-cache et un decode plus bas. La queue se remplit, mais pas parce que le serveur encaisse tant en même temps. Il en vient juste à bout moins vite.
Je veux creuser ça davantage avec des réglages de scheduler et du prefix caching. Les chiffres bruts et les définitions des tests sont dans l’arena pour que je puisse mesurer les futures runs à la même barre.
Comparaison avec Gemma-4-26B-A4B
Nemotron-NVFP4 est en single-user presque deux fois plus rapide que Gemma-NVFP4. En multi-user, la différence se réduit, mais reste en général positive.
| 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× |
Ce pattern colle à ce qu’est le modèle. Nemotron a 3B de params actifs, Gemma 4B de params actifs. En single-user, ça aide beaucoup. En multi-user, le bottleneck se déplace vers la memory bandwidth et le scheduling, et là la différence se réduit.
Ce que ça veut dire pour l’IA on-prem
Mon choix par défaut pour ce Spark, c’est NVFP4. Pas parce que 4 bits est par principe plus joli, mais parce que les chiffres sur ces workloads le portent : throughput le plus élevé, médiane la plus rapide, TPOT le plus bas, footprint le plus petit.
Je choisis FP8 quand la tail-latency compte plus que la médiane. Pense à une UI où tu veux pouvoir dire que 99 pour cent des requests démarrent dans une certaine limite. Dans les Runs H, I et J, FP8 gagne de façon constante sur le TTFT P99.
Je choisis BF16 seulement comme baseline ou pour de la validation accuracy-critique. Pas comme default de production. Pour ça, c’est trop cher sur le Spark : environ trois fois plus de mémoire que NVFP4 et grosso modo la moitié de la vitesse.
Pour un bureau de 25 personnes avec un workload chat et type RAG, je ferais tourner NVFP4, avec une suite d’eval maison à côté. Pour un chatbot externe avec une promesse de latence serrée, je testerais FP8. Pour BF16, je garderais surtout une run courte pour voir ce que la quantization change sur le fond.
Ce que ces runs ne disent pas
Pas de tests d’accuracy. FP8 et NVFP4 peuvent diverger sur le fond de BF16. Pour la production, tu dois mesurer ça sur tes propres documents, tes propres prompts et ta propre tolérance aux erreurs.
Pas de benchmarks multimodal. Nemotron-3-Nano-Omni est multimodal-aware, mais ces runs sont text-only. La vision et l’audio restent hors champ ici.
Pas de comparaison avec des modèles dense. C’est un modèle MoE. Les modèles dense donnent un ressenti différent, surtout sur la vitesse d’output et la façon dont vLLM les gère.
Pas de conclusion définitive sur le scheduler. La tail FP8-vs-NVFP4 est assez intéressante pour la tester à part avec d’autres réglages de batching et de scheduling.
Où j’atterris
Le choix de précision n’est pas un détail. Sur le Spark, il détermine si la même machine donne l’impression d’une expérience locale ou de quelque chose que tu peux confier à des collègues sans devoir expliquer toutes les cinq minutes.
NVFP4 double dans beaucoup de runs l’expérience utilisable par rapport à BF16. FP8 est moins spectaculaire, mais plus prévisible dans la tail. BF16 reste utile comme point de référence, pas comme terminus.
La leçon pratique de ces trois posts pris ensemble : suis les recettes du vendor, fais tourner l’image stable et mesure ton propre workload. Ne bricole pas toi-même sauf si tu as une bonne raison. Avec Gemma, j’avais une raison. Avec le recul, elle était médiocre.