SSD Nodes Learn 🎉 VPS dès $4.99/mois
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-07

Comment mesurer les tokens par seconde d’un LLM local

Une mesure publiée ne suffit pas. Faites varier la concurrence, mesurez les tokens par seconde avec précision et trouvez le débit minimal qui rentabilise votre GPU loué.

Pourquoi le nombre de tokens par seconde détermine la rentabilité d’un GPU

Le nombre de tokens par seconde correspond au débit auquel votre serveur produit le texte de sortie. C’est ce nombre qui permet de déterminer si la location d’un GPU coûte moins cher que l’utilisation d’une API facturée au token. Un serveur GPU est facturé à l’heure, qu’il soit utilisé ou non. Une API hébergée est facturée au token. Le GPU n’est donc rentable que si vous maintenez un débit de sortie suffisamment élevé pendant la majeure partie des heures payées.

Vous devez donc effectuer une mesure, et non vous baser sur un chiffre trouvé ailleurs. Cette page définit les quatre valeurs qu’il faut relever, puis fournit les commandes qui permettent de les obtenir et le calcul qui permet de prendre une décision.

Pourquoi le nombre de tokens par seconde publié ne correspond pas à votre valeur

DigitalOcean a publié en juillet 2026 des mesures de débit pour un seul NVIDIA H200 exécutant llama3.3-70b-instruct en FP8 (virgule flottante sur 8 bits) avec vLLM. Ces chiffres sont utiles, mais ils ne correspondent pas aux vôtres.

ChartPublished DigitalOcean H200 figures, llama3.3-70b-instruct FP8, July 2026
The data behind this chart
[
  {
    "config": "H200, one stream",
    "tok_s": "47"
  },
  {
    "config": "H100, saturated",
    "tok_s": "236"
  },
  {
    "config": "H200, saturated",
    "tok_s": "2,036"
  },
  {
    "config": "H200, saturated, in+out",
    "tok_s": "4,071.6"
  }
]

Chaque ligne ci-dessus est reprise de cette page. Deux lignes correspondent à la limite basse d’une plage indiquée sur cette page : considérez-les donc comme un minimum. Nous n’avons effectué aucune mesure présentée dans ce tableau.

Commencez par les deux dernières lignes. Le chiffre annoncé est de 4,071.6 tok/s, tandis que le débit de sortie uniquement est de 2,036 tok/s. Le chiffre annoncé compte ensemble les tokens d’entrée et de sortie. Ce test utilisait 1,024 tokens d’entrée pour 1,024 tokens de sortie : la sortie représente donc presque exactement la moitié du chiffre annoncé. Cette distinction est importante, car la facturation porte sur la sortie, qui est aussi la partie la plus lente. Le prefill (lecture du prompt) traite tous les tokens d’entrée en une seule passe. Le decode (génération de la réponse) produit un token à la fois. Un chiffre de débit total fait donc la moyenne entre une opération peu coûteuse et une opération coûteuse.

Examinez maintenant la première ligne. Le même H200, avec une seule requête à la fois, produit 47 tok/s. Le chiffre obtenu à saturation est donc plus de quarante fois supérieur avec le même matériel. Cet écart s’explique par le fait qu’à chaque étape de decode, le GPU attend la mémoire pendant la majeure partie du temps. Les requêtes concurrentes utilisent ce temps d’attente. La deuxième ligne, 236 tok/s, correspond à un seul H100 exécutant le même modèle. Il est limité par le KV cache (le cache des clés et des valeurs, c’est-à-dire la mémoire par requête qu’une conversation servie conserve sur la carte). Une carte de 80 GB peut gérer moins de requêtes simultanées avec un modèle de 70B. Elle atteint donc sa saturation à un débit inférieur.

Si vous changez de modèle ou le ratio entre les tokens d’entrée et de sortie, tous les chiffres ci-dessus changent. Les chiffres publiés servent à définir vos attentes, pas votre budget. La même règle s’applique au benchmarking honnête d’un VPS pour le disque et le réseau.

Les quatre chiffres qui comptent

  • Temps jusqu’au premier token, TTFT. Délai entre l’envoi d’une requête et l’arrivée du premier token en sortie. Il comprend le temps de prefill et le temps d’attente dans la file. C’est ce que l’utilisateur ressent directement.
  • Tokens en sortie par seconde, par flux. Vitesse d’écriture d’une réponse une fois qu’elle a commencé. Au-delà d’environ 20 tok/s, la génération est déjà plus rapide que la plupart des utilisateurs ne lisent. Un gain supplémentaire apporte donc peu.
  • Débit total de tokens en sortie à saturation. Somme des débits de tous les flux simultanés lorsque le serveur est pleinement chargé. C’est la mesure de capacité, et c’est elle qui justifie le coût du GPU.
  • TTFT p50 et p99 sous charge concurrente. p50 correspond à la requête médiane. p99 est la valeur en dessous de laquelle arrivent 99 requêtes sur 100. La mise en file se manifeste d’abord dans p99.

Les deux premières mesures s’améliorent lorsque le serveur est peu sollicité. La troisième s’améliore lorsque le serveur est chargé. Elles évoluent donc en sens opposé. C’est pourquoi aucun chiffre unique ne décrit un serveur de serving.

Définissez les longueurs d’entrée et de sortie avant de mesurer

Le débit dépend de la forme du trafic. Un prompt de 4,000 tokens avec une réponse de 50 tokens correspond à une charge principalement liée au prefill. Un prompt de 200 tokens avec une réponse de 2,000 tokens correspond à une charge principalement liée au decode. Le même serveur affiche un nombre de tokens par seconde très différent dans ces deux cas. Choisissez donc un ratio, indiquez-le à côté de chaque valeur relevée et ne comparez jamais des valeurs correspondant à des ratios différents. Le ratio 1,024 tokens en entrée pour 1,024 tokens en sortie constitue une valeur par défaut raisonnable, car plusieurs fournisseurs publient leurs résultats avec ce ratio. Si vous connaissez la répartition réelle de votre trafic, utilisez-la.

Forcez également la longueur de sortie. Un modèle qui atteint son token d’arrêt après 60 tokens produit une exécution plus courte qui semble plus rapide, car le TTFT en représente alors une part plus importante. Le flag --ignore-eos du client de benchmark vLLM force chaque requête à générer exactement le nombre de tokens demandé, afin que deux exécutions restent comparables. Le choix du modèle fait davantage varier ces valeurs que n’importe quel flag : faire tenir un modèle Qwen 3 sur le GPU d’un VPS unique traite l’aspect mémoire de ce choix.

Mesurez d’abord un seul flux

Commencez par le cas le plus simple. Cela sert de vérification de cohérence et de valeur plafond. Ollama affiche ses propres mesures.

ollama run llama3.1:8b --verbose "Write 200 words about disk scheduling."

La ligne à lire est eval rate, qui indique le nombre de tokens générés par seconde. prompt eval rate correspond au débit de prefill, et load duration au temps passé à charger le modèle dans la VRAM. Lors du premier appel après un démarrage à froid, load duration est élevé, donc total duration est trompeur. Exécutez la commande deux fois et lisez le second résultat. Par défaut, Ollama décharge un modèle inactif après cinq minutes. Une longue pause entre deux exécutions vous ramène donc au cas du démarrage à froid.

Les mêmes champs sont disponibles dans l’API, ce qui facilite l’automatisation.

curl -s http://127.0.0.1:11434/api/generate -d '{
  "model": "llama3.1:8b",
  "prompt": "Write 200 words about disk scheduling.",
  "stream": false
}' | jq '{eval_count, eval_duration,
         tok_s: (.eval_count / (.eval_duration / 1000000000))}'

eval_duration est exprimé en nanosecondes. Une division par 1,000,000,000 donne donc des secondes. C’est exactement la conversion prescrite par la documentation de l’API Ollama pour calculer le nombre de tokens par seconde. Si le serveur n’est pas encore démarré, héberger vous-même un LLM avec Ollama sur un VPS explique l’installation et l’unité systemd.

Le TTFT nécessite une requête en streaming, et curl peut mesurer cette durée.

curl -s -o /dev/null -N \
  -w 'ttfb=%{time_starttransfer}s  total=%{time_total}s\n' \
  http://127.0.0.1:8000/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"Qwen/Qwen3-8B",
       "messages":[{"role":"user","content":"Write 200 words about disk scheduling."}],
       "stream":true,"max_tokens":256}'

time_starttransfer correspond au moment où le premier octet du corps de la réponse arrive. Dans une complétion de chat en streaming, cet octet appartient au premier server-sent event. Il contient soit le premier token de contenu, soit un delta limité au rôle, envoyé juste avant celui-ci. Considérez donc cette valeur comme un TTFT à un événement près. Elle est suffisamment précise pour comparer deux exécutions sur le même serveur.

Les mesures avec un seul flux donnent deux fois une image trop favorable de la machine. Le TTFT est au meilleur niveau possible, car aucune requête ne vous précède dans la file. Le débit par flux est également au meilleur niveau possible, car toute la carte traite une seule requête. Ces deux valeurs ne disent pas quelle charge la machine peut réellement supporter.

Comment exécuter un sweep de concurrence ?

Un sweep exécute une charge fixe avec un niveau de concurrence croissant et enregistre les résultats à chaque étape. vLLM fournit le client nécessaire. Celui-ci utilise l’API OpenAI et fonctionne donc aussi avec Ollama et tout autre service compatible avec OpenAI.

vllm bench serve \
  --backend openai-chat \
  --base-url http://127.0.0.1:8000 \
  --endpoint /v1/chat/completions \
  --model Qwen/Qwen3-8B \
  --dataset-name random \
  --random-input-len 1024 \
  --random-output-len 1024 \
  --ignore-eos \
  --num-prompts 160 \
  --max-concurrency 16 \
  --percentile-metrics ttft,tpot,itl,e2el \
  --metric-percentiles 50,99

--max-concurrency limite le nombre de requêtes en cours. C’est la variable que vous faites varier. --num-prompts correspond au nombre total de requêtes envoyées. Gardez cette valeur proche de dix fois la concurrence pour obtenir une moyenne stable. Le résumé affiche Output token throughput (tok/s): et Total token throughput (tok/s):, puis Mean TTFT (ms):, Median TTFT (ms): et P99 TTFT (ms): sous une section Time to First Token.

La sortie n’indique pas le débit par flux, mais le calcul est direct. Mean TPOT (ms): correspond au temps moyen par token de sortie après le premier. Ainsi, 25 ms par token correspondent à 40 tokens par seconde et par flux. Diviser le débit de sortie par la concurrence donne le même résultat.

Répétez ensuite l’opération en enregistrant chaque exécution.

for C in 1 4 16 32 64 128; do
  vllm bench serve \
    --backend openai-chat \
    --base-url http://127.0.0.1:8000 \
    --endpoint /v1/chat/completions \
    --model Qwen/Qwen3-8B \
    --dataset-name random \
    --random-input-len 1024 \
    --random-output-len 1024 \
    --ignore-eos \
    --num-prompts $(( C * 10 )) \
    --max-concurrency "$C" \
    --percentile-metrics ttft,tpot,itl,e2el \
    --metric-percentiles 50,99 \
    --save-result --result-filename "sweep-c$C.json"
done
Lire les fichiers JSON enregistrés

Chaque exécution écrit un fichier. Extrayez donc les champs qui vous intéressent de tous les fichiers en une seule fois.

for f in sweep-c*.json; do
  jq -r --arg f "$f" \
    '[$f, .output_throughput, .total_token_throughput,
      .median_ttft_ms, .p99_ttft_ms] | @tsv' "$f"
done

output_throughput correspond au nombre de tokens de sortie par seconde. total_token_throughput réintègre les tokens d’entrée. Avec un ratio de 1:1, cette valeur est donc proche du double. p99_ttft_ms n’existe que parce que --metric-percentiles incluait 99. Si vous demandez un percentile qui n’a pas été calculé, jq affiche null.

Que montre réellement un sweep de concurrence ?

ChartIllustrative sweep shape: 8B model, one 24 GB GPU, 1024 in / 1024 out
The data behind this chart
[
  {
    "label": "1 stream",
    "per_stream_tok_s": 92,
    "total_tok_s": 92,
    "ttft_p50_ms": 48,
    "ttft_p99_ms": 61
  },
  {
    "label": "4 streams",
    "per_stream_tok_s": 88,
    "total_tok_s": 352,
    "ttft_p50_ms": 71,
    "ttft_p99_ms": 96
  },
  {
    "label": "16 streams",
    "per_stream_tok_s": 71,
    "total_tok_s": 1136,
    "ttft_p50_ms": 152,
    "ttft_p99_ms": 244
  },
  {
    "label": "32 streams",
    "per_stream_tok_s": 54,
    "total_tok_s": 1728,
    "ttft_p50_ms": 287,
    "ttft_p99_ms": 498
  },
  {
    "label": "64 streams",
    "per_stream_tok_s": 34,
    "total_tok_s": 2176,
    "ttft_p50_ms": 611,
    "ttft_p99_ms": 1240
  },
  {
    "label": "128 streams",
    "per_stream_tok_s": 18,
    "total_tok_s": 2304,
    "ttft_p50_ms": 1490,
    "ttft_p99_ms": 3820
  }
]

Ces 6 lignes illustrent la forme produite par un sweep sur une petite machine GPU louée, avec des ordres de grandeur plausibles. Elles ne mesurent pas votre serveur et ne correspondent pas à une valeur fournie par un éditeur. Exécutez la boucle ci-dessus et remplacez-les par vos propres résultats.

Observez la forme, car c’est elle qui se généralise. Avec un seul flux, la machine entière produit 92 tokens par seconde, avec un TTFT p99 de 61 ms. À 128 streams, le débit total atteint 2304 tokens par seconde, soit vingt-cinq fois plus, tandis que le débit de chaque flux tombe à 18 tokens par seconde et que le TTFT p99 atteint 3820 ms. Le débit total augmente parce que le batching transforme les attentes mémoire inutilisées en travail utile. La vitesse par flux diminue parce que les mêmes ressources de calcul sont désormais partagées.

Le dernier doublement est révélateur. Passer de 64 à 128 flux n’ajoute que moins de six pour cent au débit total, tandis que le TTFT p99 triple environ. Cela signifie que le cache KV est plein et que les requêtes sont mises en file d’attente au lieu d’être exécutées. Le point de fonctionnement utile se situe plus tôt : avec 32 flux, la machine renvoie encore 1728 tokens par seconde, soit 75 pour cent de son débit maximal, à 54 tokens par seconde et par flux, avec un TTFT p99 de 498 ms. Retenez ce point comme capacité. Le sommet de la courbe ne constitue pas un débit exploitable pour servir les utilisateurs.

Ollama et vLLM ne mesurent pas la même chose

Exécutez ce sweep sur un serveur Ollama avec sa configuration par défaut : le total variera à peine. OLLAMA_NUM_PARALLEL vaut 1 par défaut. Une seule requête s’exécute donc pendant que les autres attendent. La file d’attente fait augmenter le TTFT p99, tandis que le volume total de sortie reste stable. Augmentez cette valeur avant toute mesure.

sudo systemctl edit ollama.service
[Service]
Environment="OLLAMA_NUM_PARALLEL=8"

Redémarrez avec sudo systemctl restart ollama, puis vérifiez que le modèle tient toujours en mémoire. Chaque slot parallèle reçoit sa propre part de la fenêtre de contexte. La documentation d’Ollama précise donc qu’un contexte de 2K avec 4 requêtes parallèles alloue 8K. Si vous augmentez suffisamment le nombre de slots, le modèle déborde de la VRAM. Vérifiez ollama ps : si la colonne PROCESSOR affiche une valeur telle que 48%/52% CPU/GPU, une partie du modèle se trouve sur le CPU. Le débit diminue alors quand vous augmentez la concurrence, au lieu d’augmenter. Au-delà du nombre de slots parallèles, les requêtes sont mises en file jusqu’à OLLAMA_MAX_QUEUE, qui vaut 512 par défaut. Le serveur répond ensuite avec le code 503.

vLLM utilise le continuous batching. Il ajoute donc les nouvelles requêtes au batch en cours dès que des slots se libèrent. Sa courbe continue d’augmenter jusqu’à épuisement du KV cache. Ollama est optimisé pour un modèle, une machine et un faible coût de mise en place. Les deux moteurs donnent donc des résultats différents avec le même sweep. C’est le véritable sujet de Comparaison d’Ollama et de vLLM comme moteurs de serving. Notez le moteur et sa version pour chaque valeur mesurée.

Cinq façons de mesurer la mauvaise chose

  • Le client est distant. Effectuer le benchmark depuis votre ordinateur portable via Internet ajoute votre aller-retour réseau à chaque TTFT. Vous mesurez donc votre connexion domestique. Exécutez le client dans la même région que le serveur.
  • Le modèle était froid. La première requête inclut le chargement des poids. Avec vLLM, elle peut également inclure la capture du graphe. Envoyez un lot de préchauffage, puis ignorez le résultat.
  • Le prefix caching a répondu à votre place. vLLM active par défaut la mise en cache automatique des préfixes. Si vous envoyez toujours le même prompt, vous mesurez le cache au lieu du prefill, et le TTFT tombe à une fraction de sa valeur réelle. --dataset-name random évite ce problème, car chaque prompt est différent. Pour en avoir la certitude, démarrez le serveur avec --no-enable-prefix-caching.
  • Les sorties étaient courtes. Avec des réponses de 32 tokens, le TTFT domine chaque requête et votre nombre de tokens par seconde décrit en réalité le prefill. Utilisez --ignore-eos avec une longueur de sortie réaliste.
  • Vous avez indiqué une concurrence de 1. C’est le chiffre le plus favorable du tableau et il n’a aucune incidence sur le coût.

Transformez votre mesure en décision

Utilisez le débit de sortie saturé obtenu lors de votre sweep, et non le débit sur un seul flux, puis comparez-le au tarif par token. Le seuil de rentabilité se calcule par une simple division :

break_even_tok_s = (price_per_hour / price_per_million_output_tokens) * 1000000 / 3600

Faisons le calcul avec les tarifs de DigitalOcean en juillet 2026. Leur endpoint d’inférence dédié H200 coûtait $4.47 par heure, tandis que l’équivalent serverless coûtait $0.65 par million de tokens. 4.47 divisé par 0.65 donne 6.88 millions de tokens par heure. En divisant par 3,600 secondes, on obtient environ 1,910 tokens de sortie par seconde. Les tarifs viennent de DigitalOcean. La division est la nôtre.

Le mot décisif est soutenu. Atteindre 1,910 tokens par seconde à saturation pendant deux heures par jour ne signifie pas maintenir un débit soutenu de 1,910 tokens par seconde, car vous payez également les vingt-deux autres heures. Selon les propres calculs de DigitalOcean, le point de bascule du GPU Droplet moins cher à $3.44 par heure se situe à 72.2 % d’utilisation moyenne soutenue. En dessous de ce seuil, le tarif par token est plus avantageux. Ce sont les heures d’inactivité du GPU, et non les tokens traités lentement, qui rendent généralement l’auto-hébergement non rentable.

Votre décision repose donc sur deux données. Le sweep vous donne le plafond. Votre profil de trafic vous donne la fraction de ce plafond que vous utilisez réellement. Multipliez ces deux valeurs, puis reportez le résultat dans le seuil de rentabilité entre un GPU VPS et une API facturée au token pour déterminer la solution adaptée à votre volume.

FAQ

Quel est un bon nombre de tokens par seconde pour un LLM auto-hébergé ?

Il y a deux réponses, car cette métrique sert à deux usages. Pour une personne qui lit la sortie, un débit supérieur à environ 20 tokens de sortie par seconde et par flux est déjà plus rapide que la vitesse de lecture. Aller plus vite n’apporte donc rien. Pour le coût, le chiffre important est le débit total de sortie à pleine charge. Un bon débit est celui qui atteint votre seuil de rentabilité. Avec un tarif de $0.65 par million de tokens et une machine coûtant $4.47 par heure, ce seuil se situe autour de 1,910 tokens de sortie par seconde en continu, aux tarifs de juillet 2026. Un seul flux sur un grand modèle ne l’atteint jamais. C’est la raison d’être du batching.

Pourquoi le débit Ollama reste-t-il stable quand j’ajoute des requêtes concurrentes ?

OLLAMA_NUM_PARALLEL vaut 1 par défaut. Le serveur traite donc une seule requête à la fois par modèle et met les autres en file d’attente, jusqu’à OLLAMA_MAX_QUEUE (512 par défaut), avant de renvoyer 503. Le débit total de sortie reste stable tandis que le TTFT p99 augmente. C’est le signe caractéristique d’une file d’attente, et non d’un GPU saturé. Définissez la variable dans un drop-in systemd, puis redémarrez. Vérifiez ensuite ollama ps, car chaque slot parallèle multiplie le contexte alloué et peut déplacer une partie du modèle sur le CPU.

Dois-je mesurer le délai jusqu’au premier token ou le nombre de tokens par seconde ?

Les deux, car ces valeurs évoluent en sens inverse quand la charge augmente. Le TTFT correspond à ce que ressent l’utilisateur. Le débit de sortie à pleine charge correspond à ce qui figure sur votre facture. Relevez le TTFT p50 et p99 à chaque niveau de concurrence, puis choisissez le niveau le plus élevé pour lequel le TTFT p99 reste acceptable. Indiquez le débit obtenu à ce niveau comme capacité, et non le maximum situé au sommet de la courbe.

Un nombre plus élevé de tokens par seconde signifie-t-il toujours un coût inférieur par token ?

Non. Le coût par token correspond au prix horaire divisé par le nombre de tokens réellement produits par la machine pendant cette heure. Un serveur rapide qui reste inactif la majeure partie de la journée conserve donc un coût par token élevé. C’est le taux d’utilisation qui compte, et non la vitesse de pointe. Vérifiez également les unités : un débit total de tokens annoncé compte les tokens d’entrée. Avec un rapport entrée-sortie de 1:1, il est donc proche du double du débit de sortie facturé.

#benchmarking#tokens-per-second#vllm#ollama#gpu-vps