Comment je
mesure l'Arena.

11 tests · 2 outils · 1 GPU

La suite définit les mêmes 11 benchmarks pour chaque modèle sur le même DGX Spark : 6 tests closed-loop pour le débit contrôlé et 5 tests open-loop pour le comportement sous charge. Chaque exécution indique les tests complets, interrompus ou sans données. Complète le guide Faire tourner des LLMs sur DGX Spark.

Une seule mesure de vitesse ne montre pas comment un modèle réagit à une autre charge. Un modèle avec un débit élevé tokens/sec sur un stream peut fortement ralentir sous vingt requêtes simultanées. Un autre peut garder son throughput tout en faisant attendre le premier token. Correct pour le batch, frustrant pour le chat.

Je mesure donc le débit avec une concurrence fixe (closed-loop) et des requêtes arrivant indépendamment (open-loop). Je fais aussi varier la longueur des prompts et des réponses pour observer la vitesse et la latence selon le scénario.

11
benchmarks
3
répétitions · closed-loop
42
seed · open-loop
Aclosed-loop

llama-benchy

Comment le throughput évolue-t-il avec un nombre fixe de streams simultanés ?

Le closed-loop garde une requête active par stream : la suivante démarre dès que la précédente se termine. La suite teste des niveaux de concurrency fixes de 1 à 20. Chaque cellule tourne trois fois et est publiée en mean ± stddev.

Idéal pour : un seul utilisateur, le batch processing, la complétion de code. Pas représentatif de : une app de chat avec beaucoup de sessions simultanées.

6 / 11 benchmarks→ 01 · 02 · 03 · 04 · 05 · 06
Bopen-loop

vllm bench serve

Comment tient-il sous X users simultanés qui ne s'attendent pas les uns les autres ?

L'open-loop planifie les arrivées indépendamment de la charge déjà présente. Le request rate et la burstiness définissent le motif ; à 0,3 req/s, l'intervalle moyen est d'environ 3,3 secondes. Le générateur utilise la seed 42 pour rendre la charge reproductible.

Résultat : TTFT p50/p95 et tokens/sec par user sous charge. Je rapporte bien les chiffres de TTFT (au-delà de 2s ton app commence à sembler lente), mais ils n'éliminent aucun modèle du classement. Un test open-loop ne compte que si au moins 99 % des requêtes réussissent.

5 / 11 benchmarks→ 07 · 08 · 09 · 10 · 11

Chaque carte vient directement du contrat de benchmark versionné. Déplie "view command" pour le modèle de commande reproductible et remplace ORG/MODEL et SERVED_MODEL_NAME. Les pages modèle remplissent ces valeurs depuis les métadonnées du run.

01 · llama-benchyclosed-loop

Chat

Invite courte, réponse longue. La forme qui doit ressembler à une conversation normale ; le TTFT décide si cela paraît réactif.

pp (prompt)
1024
tg (gen)
1024
depth
0
concurrency
1 · 5 · 10
repeats
3

tokens/sec · ttft p50

3 repeats · mean ± stddevview command →hide command ↑
uvx llama-benchy==0.4.0 \
  --base-url http://localhost:8000/v1 \
  --model SERVED_MODEL_NAME \
  --pp 1024 \
  --tg 1024 \
  --depth 0 \
  --concurrency 1 5 10 \
  --runs 3 \
  --latency-mode generation \
  --format md
02 · llama-benchyclosed-loop

RAG · contexte 8k

Contexte moyen, quelques fragments de documents avec une réponse de longueur normale. Montre le coût du préremplissage sans heurter le mur.

pp (prompt)
8192
tg (gen)
512
depth
0
concurrency
5 · 10 · 20
repeats
3

tokens/sec · ttft p50

3 repeats · mean ± stddevview command →hide command ↑
uvx llama-benchy==0.4.0 \
  --base-url http://localhost:8000/v1 \
  --model SERVED_MODEL_NAME \
  --pp 8192 \
  --tg 512 \
  --depth 0 \
  --concurrency 5 10 20 \
  --runs 3 \
  --latency-mode generation \
  --format md
03 · llama-benchyclosed-loop

Sortie longue et agents

Instruction courte, beaucoup de sortie. Génération de code, rapports ou sorties structurées d'agents. Test de résistance du débit de décodage.

pp (prompt)
256
tg (gen)
4096
depth
0
concurrency
1 · 5 · 10
repeats
3

tokens/sec · ttft p50

3 repeats · mean ± stddevview command →hide command ↑
uvx llama-benchy==0.4.0 \
  --base-url http://localhost:8000/v1 \
  --model SERVED_MODEL_NAME \
  --pp 256 \
  --tg 4096 \
  --depth 0 \
  --concurrency 1 5 10 \
  --runs 3 \
  --latency-mode generation \
  --format md
04 · llama-benchyclosed-loop

Travail de bureau multi-tours

Cinq tours par conversation, dix conversations en parallèle. Proche de l'usage réel d'une équipe, avec un contexte qui grandit à chaque tour.

pp (prompt)
2048
tg (gen)
512
depth
4
concurrency
1 · 5 · 10
repeats
3

tokens/sec · ttft p50

3 repeats · mean ± stddevview command →hide command ↑
uvx llama-benchy==0.4.0 \
  --base-url http://localhost:8000/v1 \
  --model SERVED_MODEL_NAME \
  --pp 2048 \
  --tg 512 \
  --depth 4 \
  --concurrency 1 5 10 \
  --runs 3 \
  --latency-mode generation \
  --format md
05 · llama-benchyclosed-loop

Grand contexte · 25k

Test de résistance avec de grandes invites. Pas forcément du matériel de chat, mais exactement là où le mur du préremplissage apparaît et où le TTFT s'effondre.

pp (prompt)
4096 · 8192 · 16384 · 25000
tg (gen)
256
depth
0
concurrency
1 · 5 · 10
repeats
3

tokens/sec · ttft p50

3 repeats · mean ± stddevview command →hide command ↑
uvx llama-benchy==0.4.0 \
  --base-url http://localhost:8000/v1 \
  --model SERVED_MODEL_NAME \
  --pp 4096 8192 16384 25000 \
  --tg 256 \
  --depth 0 \
  --concurrency 1 5 10 \
  --runs 3 \
  --latency-mode generation \
  --format md
06 · llama-benchyclosed-loop

Concurrence sous pression

Contexte de 25k avec vingt requêtes simultanées. Cinq et dix sont déjà couverts par le test 05 ; vingt montre où l'ordonnanceur abandonne.

pp (prompt)
25000
tg (gen)
256
depth
0
concurrency
20
repeats
3

tokens/sec · ttft p50

3 repeats · mean ± stddevview command →hide command ↑
uvx llama-benchy==0.4.0 \
  --base-url http://localhost:8000/v1 \
  --model SERVED_MODEL_NAME \
  --pp 25000 \
  --tg 256 \
  --depth 0 \
  --concurrency 20 \
  --runs 3 \
  --latency-mode generation \
  --format md
07 · vllm bench serveopen-loop

Référence de bureau réaliste

Jeu de données aléatoire, 4000 tokens en entrée et 500 en sortie, taux d'arrivée 0,3 avec une irrégularité de 0,7. Un bureau tranquille.

dataset
random
input / output
4000 / 500
rate (req/s)
0.3
prompts
200
burstiness
0.7

ttft p50/p95 · tokens/sec par utilisateur

200 prompts · seed 42view command →hide command ↑
docker exec vllm-bench vllm bench serve \
  --backend openai-chat \
  --base-url http://localhost:8000 \
  --endpoint /v1/chat/completions \
  --model ORG/MODEL \
  --tokenizer ORG/MODEL \
  --served-model-name SERVED_MODEL_NAME \
  --dataset-name random \
  --random-input-len 4000 \
  --random-output-len 500 \
  --random-range-ratio 0.9 \
  --num-prompts 200 \
  --request-rate 0.3 \
  --burstiness 0.7 \
  --percentile-metrics ttft,tpot,itl,e2el \
  --metric-percentiles 50,90,95,99 \
  --seed 42 \
  --save-result \
  --result-dir /tmp \
  --result-filename 07-office-baseline.json
08 · vllm bench serveopen-loop

Vraies conversations · ShareGPT

ShareGPT V3, en moyenne 228 tokens par tour, variant naturellement selon la conversation. Ce que font les vrais utilisateurs, pas une distribution synthétique.

dataset
sharegpt
rate (req/s)
0.3
prompts
250
burstiness
0.7

ttft p50/p95 · tokens/sec par utilisateur

250 prompts · seed 42view command →hide command ↑
docker exec vllm-bench vllm bench serve \
  --backend openai-chat \
  --base-url http://localhost:8000 \
  --endpoint /v1/chat/completions \
  --model ORG/MODEL \
  --tokenizer ORG/MODEL \
  --served-model-name SERVED_MODEL_NAME \
  --dataset-name sharegpt \
  --dataset-path /tmp/ShareGPT_V3.json \
  --num-prompts 250 \
  --request-rate 0.3 \
  --burstiness 0.7 \
  --percentile-metrics ttft,tpot,itl,e2el \
  --metric-percentiles 50,90,95,99 \
  --seed 42 \
  --save-result \
  --result-dir /tmp \
  --result-filename 08-sharegpt.json
09 · vllm bench serveopen-loop

Charge de raisonnement

Longues chaînes de pensée, 1k en entrée et 4k en sortie, taux d'arrivée lent de 0,2 car chaque requête coûte beaucoup de budget de décodage. Vérifie si le TTFT reste stable.

dataset
random
input / output
1024 / 4096
rate (req/s)
0.2
prompts
50
burstiness
1

ttft p50 · tokens/sec soutenus

50 prompts · seed 42view command →hide command ↑
docker exec vllm-bench vllm bench serve \
  --backend openai-chat \
  --base-url http://localhost:8000 \
  --endpoint /v1/chat/completions \
  --model ORG/MODEL \
  --tokenizer ORG/MODEL \
  --served-model-name SERVED_MODEL_NAME \
  --dataset-name random \
  --random-input-len 1024 \
  --random-output-len 4096 \
  --random-range-ratio 0.9 \
  --num-prompts 50 \
  --request-rate 0.2 \
  --burstiness 1.0 \
  --percentile-metrics ttft,tpot,itl,e2el \
  --metric-percentiles 50,90,95,99 \
  --seed 42 \
  --save-result \
  --result-dir /tmp \
  --result-filename 09-reasoning.json
10 · vllm bench serveopen-loop

Pic du lundi matin

Aléatoire, 4000 en entrée et 500 en sortie, taux d'arrivée de 1,5 par seconde avec une irrégularité de 1,0 et 25 en parallèle au maximum. Un flux de requêtes indépendant et soutenu.

dataset
random
input / output
4000 / 500
rate (req/s)
1.5
prompts
300
burstiness
1
max parallel
25

ttft p95/p99 · profondeur de file

300 prompts · seed 42view command →hide command ↑
docker exec vllm-bench vllm bench serve \
  --backend openai-chat \
  --base-url http://localhost:8000 \
  --endpoint /v1/chat/completions \
  --model ORG/MODEL \
  --tokenizer ORG/MODEL \
  --served-model-name SERVED_MODEL_NAME \
  --dataset-name random \
  --random-input-len 4000 \
  --random-output-len 500 \
  --random-range-ratio 0.9 \
  --num-prompts 300 \
  --request-rate 1.5 \
  --burstiness 1.0 \
  --max-concurrency 25 \
  --percentile-metrics ttft,tpot,itl,e2el \
  --metric-percentiles 50,90,95,99 \
  --seed 42 \
  --save-result \
  --result-dir /tmp \
  --result-filename 10-monday-peak.json
11 · vllm bench serveopen-loop

Capacité · balayage de débit

Six taux d'arrivée croissants, de 0,1 à 1,0 requête par seconde. À chaque palier, on vérifie si le TTFT p95 reste sous le seuil.

dataset
random
input / output
4000 / 500
rates (req/s)
0.1 · 0.2 · 0.3 · 0.5 · 0.7 · 1.0
prompts
100 · 100 · 100 · 125 · 175 · 250
burstiness
0.7

req/s sous le seuil TTFT p95

6 rates · seed 42view command →hide command ↑
# 0.1 req/s
docker exec vllm-bench vllm bench serve \
  --backend openai-chat \
  --base-url http://localhost:8000 \
  --endpoint /v1/chat/completions \
  --model ORG/MODEL \
  --tokenizer ORG/MODEL \
  --served-model-name SERVED_MODEL_NAME \
  --dataset-name random \
  --random-input-len 4000 \
  --random-output-len 500 \
  --random-range-ratio 0.9 \
  --num-prompts 100 \
  --request-rate 0.1 \
  --burstiness 0.7 \
  --percentile-metrics ttft,tpot,itl,e2el \
  --metric-percentiles 50,90,95,99 \
  --seed 42 \
  --save-result \
  --result-dir /tmp \
  --result-filename 11-rate-sweep-0.1.json

# 0.2 req/s
docker exec vllm-bench vllm bench serve \
  --backend openai-chat \
  --base-url http://localhost:8000 \
  --endpoint /v1/chat/completions \
  --model ORG/MODEL \
  --tokenizer ORG/MODEL \
  --served-model-name SERVED_MODEL_NAME \
  --dataset-name random \
  --random-input-len 4000 \
  --random-output-len 500 \
  --random-range-ratio 0.9 \
  --num-prompts 100 \
  --request-rate 0.2 \
  --burstiness 0.7 \
  --percentile-metrics ttft,tpot,itl,e2el \
  --metric-percentiles 50,90,95,99 \
  --seed 42 \
  --save-result \
  --result-dir /tmp \
  --result-filename 11-rate-sweep-0.2.json

# 0.3 req/s
docker exec vllm-bench vllm bench serve \
  --backend openai-chat \
  --base-url http://localhost:8000 \
  --endpoint /v1/chat/completions \
  --model ORG/MODEL \
  --tokenizer ORG/MODEL \
  --served-model-name SERVED_MODEL_NAME \
  --dataset-name random \
  --random-input-len 4000 \
  --random-output-len 500 \
  --random-range-ratio 0.9 \
  --num-prompts 100 \
  --request-rate 0.3 \
  --burstiness 0.7 \
  --percentile-metrics ttft,tpot,itl,e2el \
  --metric-percentiles 50,90,95,99 \
  --seed 42 \
  --save-result \
  --result-dir /tmp \
  --result-filename 11-rate-sweep-0.3.json

# 0.5 req/s
docker exec vllm-bench vllm bench serve \
  --backend openai-chat \
  --base-url http://localhost:8000 \
  --endpoint /v1/chat/completions \
  --model ORG/MODEL \
  --tokenizer ORG/MODEL \
  --served-model-name SERVED_MODEL_NAME \
  --dataset-name random \
  --random-input-len 4000 \
  --random-output-len 500 \
  --random-range-ratio 0.9 \
  --num-prompts 125 \
  --request-rate 0.5 \
  --burstiness 0.7 \
  --percentile-metrics ttft,tpot,itl,e2el \
  --metric-percentiles 50,90,95,99 \
  --seed 42 \
  --save-result \
  --result-dir /tmp \
  --result-filename 11-rate-sweep-0.5.json

# 0.7 req/s
docker exec vllm-bench vllm bench serve \
  --backend openai-chat \
  --base-url http://localhost:8000 \
  --endpoint /v1/chat/completions \
  --model ORG/MODEL \
  --tokenizer ORG/MODEL \
  --served-model-name SERVED_MODEL_NAME \
  --dataset-name random \
  --random-input-len 4000 \
  --random-output-len 500 \
  --random-range-ratio 0.9 \
  --num-prompts 175 \
  --request-rate 0.7 \
  --burstiness 0.7 \
  --percentile-metrics ttft,tpot,itl,e2el \
  --metric-percentiles 50,90,95,99 \
  --seed 42 \
  --save-result \
  --result-dir /tmp \
  --result-filename 11-rate-sweep-0.7.json

# 1.0 req/s
docker exec vllm-bench vllm bench serve \
  --backend openai-chat \
  --base-url http://localhost:8000 \
  --endpoint /v1/chat/completions \
  --model ORG/MODEL \
  --tokenizer ORG/MODEL \
  --served-model-name SERVED_MODEL_NAME \
  --dataset-name random \
  --random-input-len 4000 \
  --random-output-len 500 \
  --random-range-ratio 0.9 \
  --num-prompts 250 \
  --request-rate 1.0 \
  --burstiness 0.7 \
  --percentile-metrics ttft,tpot,itl,e2el \
  --metric-percentiles 50,90,95,99 \
  --seed 42 \
  --save-result \
  --result-dir /tmp \
  --result-filename 11-rate-sweep-1.0.json
GPU
NVIDIA DGX Spark
128 GB unified · GB10 Blackwell · NVFP4 natif
Server
vLLM
version exacte par run · prefix caching désactivé
Quant
BF16 · FP8 · NVFP4
chaque modèle dans les précisions disponibles
OS
Ubuntu 24.04
l'image Docker et le driver sont dans les métadonnées
Agrégation
3 répétitions · closed-loop
mean ± stddev par cellule mesurée

Les commandes sont construites de manière déterministe à partir du contrat versionné et des métadonnées de chaque run.

Démarre d'abord un serveur vLLM compatible OpenAI avec le modèle et la configuration indiqués dans les métadonnées du run. Utilise ensuite la commande de la page modèle. Le test 08 attend ShareGPT V3 dans /tmp/ShareGPT_V3.json ; les autres tests open-loop génèrent des prompts random synthétiques.

Les runs historiques n'ont pas enregistré l'argv original mot pour mot. Les commandes publiées sont donc signalées comme des reconstructions du contrat et des métadonnées. Le lien source fixe les deux révisions Git utilisées.

  • Contrat et source du run fixés à des révisions Git
  • Model id, served name et versions issus des métadonnées
  • Closed-loop : 3 répétitions publiées en mean ± stddev
  • Open-loop : seed 42, percentiles p50/p90/p95/p99 et au moins 99 % de request success
  • Résultats open-loop bruts enregistrés en JSON dans /tmp
  • Prefix caching désactivé dans les runs publiés

Un modèle que tu
aimerais voir dans la suite ?

Envoie un lien Hugging Face ou une configuration vLLM. Si le modèle tient sur le Spark, je peux le comparer avec les mêmes tests.