
Combien coûte réellement la gestion d’un LLM local ? (Euros par million de jetons, mesurés)
donc c’est fondamentalement gratuit. Je l’ai dit, vous l’avez probablement dit, et c’est le genre d’affirmation qui semble vraie jusqu’à ce que quelqu’un la mesure. Alors je l’ai mesuré.
La boîte est « ardi » – une machine openSUSE avec un seul RTX 3090 (24 Go). J’y ai effectué un test de référence contrôlé : trois modèles locaux servis par ollama, chacun piloté sous une charge de travail fixe identique, tandis qu’un tableau de bord tarifé à chaque exécution par le la vraie énergie du GPU qu’il a brûlée. Pas une estimation de la puissance thermique de conception, pas une estimation au dos de l’enveloppe – la puissance est échantillonnée à partir de nvidia-smi toutes les 10 secondes et intégrée sur la fenêtre de début → fin exacte de chaque exécution, à mon tarif d’électricité réel jour/nuit : 0,30 BGN jour / 0,18 BGN nuit (Lev bulgare – mon taux de services publics réel ; converti en EUR tout au long de cet article au taux fixe de la BCE, 1 BGN ≈ 0,5113 €).
Le résultat est un chiffre par modèle : des euros par million de jetons de sortie. Cinq des huit modèles que j’ai mesurés étaient moins chers qu’une API cloud hébergée. Trois ne l’ont pas fait – et ce ne sont pas les trois que vous devineriez à partir du seul nombre de paramètres.
L’affirmation que je voulais tester
La sagesse populaire veut que l’inférence locale soit l’option la moins chère : vous avez acheté le GPU, le jeton marginal est gratuit, le cloud est la chose avec le compteur en marche. Chaque partie mérite un numéro, alors voici celle que j’ai recherchée : le coût énergétique marginal de la génération locale d’un tokenmodèle par modèle, et comment il se compare à ce qu’une API de classe « Flash » hébergée facture pour le même jeton.
Pour en faire une mesure et non un argument, j’avais besoin de trois choses constantes : la même charge de travail pour chaque modèle, le même matériel et un compteur fiable pour lire le GPU à chaque exécution. Le troisième est la partie la plus difficile à la maison – c’est là que l’instrument entre en jeu.

La méthode
Trois modèles, choisis pour couvrir la gamme de tailles que je garde réellement sur la boîte : gemma3:1b (minuscule), gemma4:26b (Gemma 4 de Google–25,8B paramètres sous le capot ; ollama l’arrondit simplement à « 26b » dans la balise), et gemma3:27b (le plus ancien et le plus grand). Tous les trois sont des poids GGUF quantifiés Q4_K_M extraits via ollama, donc la comparaison est identique sur la quantification. Chacun a obtenu le même charge de travail fixe — une boucle de 256 générations de jetons parcourant cinq invites fixes — maintenue pendant ~4 minutes (240 s)le GPU a donc atteint un état stable plutôt que d’être jugé sur un premier appel à froid.
Pendant que chaque modèle fonctionnait, je l’ai suivi comme un expérience tarifée dans HomeLab Monitor – mon propre tableau de bord, open source, MIT, un conteneur. Il échantillonne la puissance du GPU de nvidia-smi toutes les 10 s, et pour une exécution suivie, il intègre la puissance sur la fenêtre de début → fin de l’exécution en kWh, puis la multiplie par le tarif en vigueur (jour ou nuit) – dans la devise avec laquelle je l’ai configuré, BGN . Cela convertit « combien a coûté ce modèle ? d’une supposition à un chiffre mesuré ; Je convertis ensuite en EUR pour plus de lisibilité.
Ensuite, l’arithmétique qui transforme une course de 4 minutes en un taux comparable :
€ per 1M output tokens = (run_cost_BGN × 0.5113) ÷ (output_tokens / 1,000,000)
Le benchmark de génération de trois modèles ci-dessous est reproductible de bout en bout. Voici comment le tenir debout sur votre propre boîte.
Étape 1 : afficher le moniteur (un conteneur)
Sur la machine avec le GPU :
COMPOSE_URL=https://raw.githubusercontent.com/SikamikanikoBG/homelab-monitor/main/docker-compose.yml
curl -fsSLO "$COMPOSE_URL"
docker compose up -d
Ouvrez http://
Étape 2 : créer une clé d’ingestion
Le script de référence doit pousser les exécutions dans le hub, il a donc besoin d’une clé. Paramètres → Intégrations → Créer une clé. La clé (une chaîne hlm_…) est affichée une fois — copiez-la ensuite, vous ne la reverrez plus. Conservez-le dans une variable d’environnement ; ne le collez pas dans le script.
export HOMELAB_KEY="hlm_xxxxxxxxxxxxxxxxxxxx"

Le même panneau a un Télécharger le client lien pour le fichier Python que vous utiliserez à l’étape 3 et un démarrage rapide copier-coller – cet écran couvre donc les deux étapes suivantes.
Étape 3 : récupérez le petit client
Le hub sert son propre client Python. C’est uniquement stdlib – rien à installer pip :
CLIENT_URL=http://<hub>:9800/static/homelab_run.py
curl -O "$CLIENT_URL"
Ce fichier constitue l’intégralité de l’API de suivi. Le modèle est délibérément petit : configurez une fois, puis enveloppez n’importe quel bloc de travail dans un gestionnaire de contexte d’exécution et enregistrez les métriques à l’intérieur.
import homelab_run as homelab
homelab.configure(url="http://<hub>:9800", key="hlm_...")
with homelab.run("llm-cost-gemma3-1b",
params={"model": "gemma3:1b", "engine": "ollama",
"gpu": "RTX 3090 24GB", "num_predict": 256}) as r:
# ... do the work ...
r.log_metric("tokens_per_sec", tps, step=i)
Lorsque le bloc with s’ouvre, le hub marque le début d’une exécution ; quand il se ferme, cela marque la fin et prix l’énergie GPU brûlée entre les deux. La fenêtre est la mesure.
Étape 4 – rédiger le benchmark
La charge de travail est simplement /api/generate d’ollama en boucle, lisant les champs en arrière pour obtenir réel jetons par seconde plutôt qu’une approximation d’horloge murale. Les deux domaines qui comptent sont eval_count (jetons générés) et eval_duration (nanosecondes passées à les générer) :
import json, time, urllib.request
OLLAMA = "http://localhost:11434"
def ollama_generate(model, prompt):
body = json.dumps({
"model": model, "prompt": prompt, "stream": False,
"options": {"num_predict": 256, "temperature": 0.7},
}).encode()
req = urllib.request.Request(OLLAMA + "/api/generate", data=body,
headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=600) as resp:
return json.loads(resp.read())
Conduisez ensuite chaque modèle sous charge pendant une durée fixe, enveloppé dans un parcours suivi :
def bench(model, duration_s=240):
ollama_generate(model, "Say hello.") # warm up - exclude the cold start
run = homelab.run(f"llm-cost-{model.replace(':', '-')}",
params={"model": model, "engine": "ollama",
"gpu": "RTX 3090 24GB", "num_predict": 256,
"workload": "fixed-prompt-loop"},
tags=["llm-cost", "benchmark"])
tot_out = 0
t0 = time.time(); step = 0
with run as r:
while time.time() - t0 < duration_s:
res = ollama_generate(model, PROMPTS[step % len(PROMPTS)])
ec, ed = res.get("eval_count", 0), res.get("eval_duration", 1) # ed in ns
tps = ec / (ed / 1e9) if ed else 0.0
tot_out += ec
r.log_metric("tokens_per_sec", round(tps, 2), step=step)
step += 1
r.log_params({"total_output_tokens": tot_out})
return run.id, tot_out
Un appel d’échauffement avant la fenêtre chronométrée maintient la latence de démarrage à froid hors de la mesure. Le time.sleep(15) que j’ai mis entre les modèles (non illustré) maintient chaque fenêtre de prix proprement séparée afin que la queue d’un modèle ne coule pas dans la facture du suivant.
Étape 5 – lire le retour en arrière, tarifé
Exécutez le script pour les trois modèles et ouvrez le Expérienceslanguette. Chaque exécution est désormais une ligne portant sa véritable énergie et son coût :

Cliquez sur une exécution et vous obtenez la courbe de puissance du GPU pour cette fenêtre exacteavec le prix en dessous – la matière première à partir de laquelle le chiffre en euros est calculé :

Le hub expose également le coût de chaque exécution par programmation, de sorte que la division finale peut avoir lieu dans le script :
pulled = homelab.pull(run_id) # cost, energy_kwh, avg_w, power curve - cost is in BGN
eur_per_mtok = round((pulled["cost"] * 0.5113) / (tot_out / 1e6), 4)
Les chiffres
Plus tard, je suis revenu en arrière et j’ai ajouté cinq autres modèles – GLM-4.5-Air (106B), DeepSeek-R1-Distill (32.8B), Seed-OSS (36B), Devstral (24B) et Qwen3-Coder (30.5B) – tirés d’un benchmark d’agent de codage distinct que j’avais déjà exécuté sur le même boîtier (même GPU, même tarif, même méthode de coût-jetons, mais de véritables tâches d’agent en plusieurs étapes au lieu d’un dédié). boucle de génération de 240 secondes — notée là où cela compte, dans les mises en garde ci-dessous). Huit modèles, une échelle :


Et la référence cloud par rapport à laquelle je mesurais (prix catalogue, juin 2026) : un modèle hébergé de classe Flash – Gemini 2.5 Flash, GPT-4o-mini – fonctionne environ 0,60 $ par million de jetons de sortie (~ 0,55 €); le niveau le moins cher (Gemini 3.1 Flash-Lite) est d’environ 0,40 $.
La surprise
Cinq des huit sont moins chers que la référence cloud. Trois ne le sont pas – et les trois qui ne le sont pas le sont pas les trois que vous choisiriez par nombre de paramètres.
GLM-4.5-Air est le plus gros modèle de la liste, 106 milliards de paramètres, et il coûte 1 040 €/1 M – véritablement plus que l’API cloud, l’électricité seule, avant qu’un centime du prix d’achat du GPU ne soit pris en compte. Cette partie correspond à l’intuition initiale : gros modèle, lent, cher.
Mais DeepSeek-R1-Distill fait à peine un tiers de la taille de GLM – 32,8 milliards de paramètres – et c’est le la plupart modèle cher sur toute la liste, à 1,526€/1M. Il consomme moins d’énergie que le gemma3:27b (155 W contre 283 W) – et reste le le plus cher par jeton de tous les modèles testés .
Une note méthodologique qui compte ici, dès le départ plutôt qu’enfouie : les trois modèles Gemma ont été mesurés sur un benchmark dédié à génération soutenue de 240 secondes que j’ai construit pour cette pièce. Les cinq autres – y compris DeepSeek-R1-Distill – proviennent d’un benchmark antérieur et distinct de tâches réelles d’agent de codage en plusieurs étapes (appels d’outils, raisonnement multi-tours) sur le même GPU et le même tarif, en utilisant la même formule de jetons coût/sortie. Cette distinction est la seule raison pour laquelle DeepSeek ressemble à ce qu’il est : son vitesse de génération brute lors de la production active de jetons, le taux respectable est de 6,7 jetons/s. Mais la vitesse de génération brute n’est pas ce que facture le compteur. Le compteur facture le temps passé sur la carte, et DeepSeek – un modèle distillé par le raisonnement – passe la majeure partie de ce temps à délibérer entre les générations, sans générer. C’est taux de livraison effectif(jetons de sortie ÷ temps d’exécution total, délibération incluse) revient à 3,7 jetons/s le plus bas de tous les modèles testés – bien en dessous de l’effectif 7,1 de Seed-OSS et de 5,0 de GLM. C’est ce que suit réellement le prix.
La taille n’a jamais été le mécanisme. La vitesse effective était – et la vitesse effective n’est pas le nombre auquel ces modèles sont habituellement comparés.
Le mécanisme
Chaque jeton coûte des watts ÷ débit.C’est toute l’équation : l’astuce consiste à utiliser le bon numéro de débit :

gemma3:1b est rapide (136 tok/s) et léger (154 W) – bon marché sur les deux axes, globalement le moins cher. Devstral tire le plus de puissance de tous les modèles testés (320 W) mais reste au milieu du tableau car il est encore raisonnablement rapide. Pour les trois modèles Gemma spécialement conçus, vitesse de génération brute est le nombre qui fixe le prix – watts ÷ tok bruts/s reconstruit presque exactement le rapport de coût. Pour les cinq modèles de référence agent, le nombre qui fixe le prix est débit d’horloge mural efficace pas la vitesse de génération brute à laquelle ils seraient normalement cités – et par ce nombre, Seed-OSS (7,1 tok/s effectifs), GLM-4,5-Air (5,0) et DeepSeek-R1-Distill (3,7) sont les trois modèles les plus lents testés, exactement dans l’ordre dans lequel leur coût les classe. Aucun d’entre eux n’a une consommation électrique inhabituelle (141-186 W, en fait). inférieur que plusieurs modèles moins chers); ils sont simplement lents une fois que les appels d’outils et les lacunes de raisonnement sont comptés, et un modèle lent paie pour chaque seconde où il détient la carte, quel que soit le nombre de paramètres.
Ce que je ne prétends *pas*
C’est la partie qui reste honnête, donc je vais être franc sur les bords :
· Il s’agit uniquement de l’électricité du GPU – mesurée, marginale et minuscule.Une course complète arrondit à une fraction de cent. Je ne mesure pas le CPU ou la DRAM (RAPL était illisible sur cet hôte, donc ceux-ci sont revenus nuls), et je suis explicitement pas en comptant le prix d’achat du GPU, la consommation inactive, le refroidissement ou mon temps. Les chiffres €/1M sont les tarif énergétique marginal utile pour déterminer quel modèle est le moins cher à continuer à générer, et non pour déterminer le coût total de possession.
· Le coût réel du local est le GPU que vous avez acheté, pas les watts. Un 3090 ne s’amortit que dans le cadre d’une utilisation élevée et régulière. Un boîtier occupé quelques minutes par jour paie principalement pour du silicium inutilisé, et aucun chiffre sur l’électricité ne reflète cela. La lentille de l’énergie marginale est honnête quant à sa propre portée : c’est le plancher, pas la facture.
· Deux méthodes de mesure (voir « la surprise » ci-dessus pour savoir pourquoi c’est important). Les trois modèles Gemma : une référence dédiée à la génération soutenue de 240 secondes. Les cinq autres : un véritable benchmark d’agent de codage distinct, même formule coût-jetons, mais efficace le débit de l’horloge murale plutôt que la vitesse de génération brute – ce qui est sans doute plus représentatif de la façon dont ces modèles sont réellement utilisés, et non moins, puisque les charges de travail réelles des agents incluent des appels d’outils et des délibérations qu’une boucle de génération pure ne ferait jamais.
·Un GPU, un moteur, des poids GGUF quantifiés.Votre tarif, modèle, taille de lot, quantification et carte déplaceront chaque numéro ici. Le tableau est mon boîte; la méthode est la partie transférable.
Considérez donc les euros absolus comme indicatifs et le forme comme résultat : le coût par jeton suit la vitesse de livraison effective, et cela ne suit pas le nombre de paramètres comme vous le souhaiteriez.
Les plats à emporter
Choisissez le modèle le plus petit et le plus rapide qui franchit votre barre de qualité – c’est là que le « local » paie réellement, de loin, et cela n’a rien à voir avec le choix d’un petit modèle. parce que c’est petit. Qwen3-Coder à 30,5B est moins cher par jeton que gemma4:26b, et gemma3:27b à 27B bat tous les modèles de style de raisonnement de la liste bien qu’il s’agisse d’un modèle simple et dense sans astuces d’efficacité particulières. La variable qui compte, ce sont les jetons livrés par seconde d’horloge murale, point final – et non le nombre brut de vitesse de génération auquel un modèle est habituellement comparé – et la seule façon de le connaître pour votre modèle et votre boîte est de le mesurer.
L’instrument que j’ai utilisé est Moniteur de laboratoire à domicile– open source, MIT, un conteneur – et le prix d’exécution ci-dessus correspond exactement à ce que fait son onglet Expériences :
Ajoutez votre hôte GPU, créez une clé et vos prochains prix de référence lui-même – dans la devise que vous avez réellement configurée, ce qui vaut la peine de vérifier avant de publier le numéro.
Donc : lorsque vous accédez au modèle local via l’API, connaissez-vous réellement votre numéro par jeton, dans la bonne devise – ou êtes-vous, comme je l’étais, en supposant simplement que le GPU le rend gratuit ?



