vLLM : Maîtriser l'Inference Haute Performance pour les LLM
Mise à jour :
Les modèles de langage deviennent de plus en plus imposants et gourmands en ressources, vLLM (Virtual Large Language Model) représente une révolution dans le domaine de l’inférence. Développé par l’équipe de UC Berkeley, ce moteur d’inférence résout les principaux défis de performance rencontrés en production.
Les défis actuels des LLM en production :
- Latence élevée : Les modèles prennent trop de temps à répondre
- Utilisation mémoire excessive : Les GPU saturent rapidement
- Faible débit : Peu de requêtes traitées simultanément
- Coûts prohibitifs : Infrastructure surdimensionnée nécessaire
vLLM apporte des solutions concrètes à ces problématiques grâce à ses innovations techniques.
Qu’est-ce que vLLM ?
vLLM est une bibliothèque d’inférence haute performance développée par UC Berkeley. Elle révolutionne le serving de LLM grâce à PagedAttention, offrant jusqu’à 24x plus de débit que HuggingFace Transformers.
Voici les Avantages clés de vLLM :
- Gestion optimisée de la mémoire avec PagedAttention
- Traitement par lots (batch processing) intelligent
- Compatibilité étendue avec les modèles les plus populaires
- API compatible OpenAI pour l’utilisation dans vos ecosystèmes
Prérequis Techniques
En fonction du modèle que vous souhaitez déployer, les exigences matérielles peuvent varier. Cependant, voici quelques recommandations générales pour une installation optimale de vLLM pour des modèles de grande taille :
- CPU : Processeur multi-cœurs récent (Intel Xeon ou AMD EPYC) avec 32 threads ou plus
- GPU : Au moins une carte NVIDIA avec 32 Go de VRAM
- RAM : Minimum 32 Go de RAM système
- Stockage : SSD rapide avec au moins 500 Go d’espace libre
- Système d’exploitation : Ubuntu 22.04 LTS
Pourquoi Utiliser autant de CPU/RAM ?
Les modèles de langage, en particulier ceux de grande taille, nécessitent une quantité significative de ressources CPU et RAM pour plusieurs raisons :
- Traitement parallèle : Les modèles de langage exploitent le parallélisme pour accélérer l’inférence. Plus de cœurs CPU permettent de gérer plusieurs threads simultanément, réduisant ainsi la latence.
- Gestion de la mémoire : Les modèles volumineux requièrent une gestion efficace de la mémoire pour stocker les poids du modèle et les données intermédiaires. Une RAM suffisante évite les goulots d’étranglement et les erreurs d’allocation mémoire.
- Prétraitement et post-traitement : Les opérations de prétraitement des entrées et de post-traitement des sorties peuvent être intensives en CPU, surtout lorsqu’il s’agit de manipuler de grandes quantités de texte.
- Support des charges de travail : Dans un environnement de production, plusieurs requêtes peuvent être traitées simultanément. Une configuration robuste en CPU et RAM garantit que le système peut gérer ces charges sans dégradation des performances.
Donc si vous prévoyez de déployer des modèles de langage de grande taille en production, il est crucial de disposer d’une infrastructure matérielle adéquate pour assurer des performances optimales et une expérience utilisateur fluide.
Installation et Configuration Avancée
Passons à la pratique avec une installation optimisée et une configuration avancée de vLLM. Je ne suis pas spécialiste mais j’ai essayé de compiler les meilleures pratiques issues de la documentation officielle et de la communauté.
- Vérification que les GPU sont bien des reconnus :
lspci | grep -i nvidia
Cette commande doit lister les GPU NVIDIA installés, par exemple :
13:00.0 3D controller: NVIDIA Corporation Device 2331 (rev a1)14:00.0 3D controller: NVIDIA Corporation Device 2331 (rev a1)
On peut passer à l’installation.
- Mise à jour du système :
Commencer par mettre à jour le système :
sudo apt update && sudo apt upgrade -y
- Installation du kernel et des paquets essentiels :
Les GPU récents comme les H100 nécessitent une version de noyau ≥ 6.2.
sudo apt install -y linux-generic-hwe-22.04 linux-headers-generic-hwe-22.04reboot
Vérifier la version du noyau après le redémarrage :
uname -r6.8.0-79-generic # doit afficher 6.8.x ou plus
Installer les outils de base :
sudo apt install -y build-essential dkms wget curl git \ htop iotop sysstat numactl cpufrequtils irqbalance \ tuned ethtool net-tools python3-pip python3-venv python3-dev \ freeglut3-dev libx11-dev libxmu-dev libxi-dev \ libglu1-mesa-dev libfreeimage-dev libglfw3-dev
- Installation des Pilotes NVIDIA
Ajouter le dépôt CUDA :
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.debsudo dpkg -i cuda-keyring_1.1-1_all.debsudo apt update
Installer drivers et CUDA :
sudo apt install -y cuda-drivers cuda-toolkit
Ajouter les chemins CUDA aux variables d’environnement.
# Retrait de toutes anciennes versions de CUDA dans le fichier .bashrcsed -i '/^export PATH=\/usr\/local\/cuda/d' ~/.bashrc
# Détection automatique de la version CUDA installéeCUDA_PATH=$(find /usr/local -maxdepth 1 -name "cuda-*" -type d | sort -V | tail -1)
# Si pas de version spécifique trouvée, utiliser le lien symboliqueif [ -z "$CUDA_PATH" ]; then CUDA_PATH="/usr/local/cuda"fi
echo "Version CUDA détectée : $CUDA_PATH"
# Ajout des chemins aux variables d'environnementecho "export PATH=$CUDA_PATH/bin:\$PATH" >> ~/.bashrcecho "export LD_LIBRARY_PATH=$CUDA_PATH/lib64:\$LD_LIBRARY_PATH" >> ~/.bashrcecho "export CUDA_HOME=$CUDA_PATH" >> ~/.bashrc
# Rechargement du profilsource ~/.bashrc
# On reboot pour s'assurer que tout est pris en comptesudo reboot
Vérifier que vos GPU sont bien reconnus :
nvidia-smi
Cette commande doit afficher les GPU installés avec leurs détails, par exemple :
Wed Sep 17 12:55:57 2025+-----------------------------------------------------------------------------------------+| NVIDIA-SMI 580.82.07 Driver Version: 580.82.07 CUDA Version: 13.0 |+-----------------------------------------+------------------------+----------------------+| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC || Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. || | | MIG M. ||=========================================+========================+======================|| 0 NVIDIA H100 PCIe On | 00000000:13:00.0 Off | 0 || N/A 37C P0 51W / 350W | 0MiB / 81559MiB | 0% Default || | | Disabled |+-----------------------------------------+------------------------+----------------------+| 1 NVIDIA H100 PCIe On | 00000000:14:00.0 Off | 0 || N/A 35C P0 49W / 350W | 0MiB / 81559MiB | 0% Default || | | Disabled |+-----------------------------------------+------------------------+----------------------+
+-----------------------------------------------------------------------------------------+| Processes: || GPU GI CI PID Type Process name GPU Memory || ID ID Usage ||=========================================================================================|| No running processes found |+-----------------------------------------------------------------------------------------+
Ici, on voit que 2× H100 sont correctement détectées.
Optimisations
Nous allons configurer les services pour optimiser les performances GPU. Toutes ces optimisations sont optionnelles, mais recommandées pour un usage en production.
- Activation du mode persistant :
Le mode persistant évite que les GPU ne se mettent en veille, ce qui peut introduire de la latence.
sudo systemctl enable --now nvidia-persistenced
- Paramétrage du scheduler CPU en mode performance :
echo 'GOVERNOR="performance"' | sudo tee /etc/default/cpufrequtilssudo systemctl restart cpufrequtils
- Définition des Hugepages :
Les Hugepages améliorent la gestion de la mémoire pour les applications intensives comme vLLM.
echo 64 | sudo tee /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepagesecho 8192 | sudo tee /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
- Tuned Profil :
Tuned ajuste automatiquement les paramètres système pour des performances optimales.
sudo tuned-adm profile latency-performancetuned-adm activesudo reboot # On reboot pour appliquer les changements
Installation de VLLM
Le plus dur est fait, on peut maintenant installer vLLM dans un environnement Python dédié.
- Commençons par créer un utilisateur dédié pour exécuter vLLM en production :
sudo adduser --disabled-password --gecos "" vllmusersudo usermod -aG sudo vllmusersudo su - vllmuser
- Création de l’environnement virtuel Python :
cdpython3 -m venv ~/venvsource ~/venv/bin/activate
- Installer les packages vLLM et ses dépendances :
pip install --upgrade pip setuptools wheelpip install vllm mistral_common transformers tokenizers uvloop
Important : Création du répertoire cache Triton avec bonnes permissions :
# Création du cache Triton pour les kernels compilésmkdir -p ~/.tritonchmod 755 ~/.triton
# Vérification que gcc est accessiblewhich gcc || echo "Erreur: gcc non trouvé"
- Vérification de l’installation :
python -c "import mistral_common; print(mistral_common.__version__)"1.8.5 # doit afficher la version installée
Lancement de vLLM avec le model Mistral-Small-3.2-24B
Pour ce tutoriel, nous allons utiliser le modèle open-source Mistral-Small-3.2-24B-Instruct-2506 ↗, qui est un modèle de 24 milliards de paramètres optimisé pour les applications de type chat.
Attention, ce modèle nécessite environ 55 Go de VRAM pour fonctionner correctement. Avec nos 2× H100 de 80 Go, nous avons une marge confortable.
vllm serve mistralai/Mistral-Small-3.2-24B-Instruct-2506 \ --distributed-executor-backend mp \ --tensor-parallel-size 2 --pipeline-parallel-size 1 \ --dtype auto \ --tokenizer-mode mistral --config-format mistral --load-format mistral \ --tool-call-parser mistral \ --gpu-memory-utilization 0.92 \ --max-model-len 4096 \ --max-num-seqs 64 \ --max-num-batched-tokens 8192 \ --swap-space 16 \ --port 8000
Attention, le premier lancement peut prendre plusieurs minutes car les poids du modèle sont téléchargés depuis HuggingFace et mis en cache localement.
Explications des paramètres :
mistralai/Mistral-Small-3.2-24B-Instruct-2506
Modèle 24 milliards de paramètres optimisé pour l’instruction-following.--distributed-executor-backend mp
Utilise le backend multiprocessing pour répartir les tâches.--tensor-parallel-size 2
Découpe les poids du modèle sur 2 GPU (utile avec tes 2× H100).--pipeline-parallel-size 1
Pas de découpage supplémentaire des couches → tout passe en tensor parallelism.--dtype auto
Choisit automatiquement le type de calcul adapté (bf16
sur H100).--tokenizer-mode mistral
Active le tokenizer officiel de Mistral.--config-format mistral
/--load-format mistral
Indique que la config et les poids suivent le format Mistral.--tool-call-parser mistral
Active le parser de tool calls propre à Mistral.--gpu-memory-utilization 0.92
Autorise vLLM à utiliser 92 % de la VRAM du GPU (évite les OOM).--swap-space 16
Active un swap CPU de 16 Go en cas de dépassement mémoire GPU.--max-model-len 4096
Contexte maximum : 4096 tokens (~3 000 mots).--max-num-seqs 64
Nombre maximum de séquences simultanées dans un batch.--max-num-batched-tokens 8192
Nombre total de tokens par batch. → Cela équilibre latence et débit (throughput).--port 8000
Le serveur écoute surhttp://localhost:8000
avec une API compatible OpenAI.
Quelques recommandations pour ajuster les paramètres de batching selon tes besoins :
Si tu veux plus des contextes plus longs, essaie par paliers :
--max-model-len
8192 avec--max-num-seqs
64,--max-num-batched-tokens
8192--max-model-len
16384 avec--max-num-seqs
48,--max-num-batched-tokens
8192--max-model-len
32768 avec--max-num-seqs
24–32,--max-num-batched-tokens
4096–8192
Si tu veux max de débit :
Garde --max-model-len
4096–8192, pousse --max-num-seqs
et
--max-num-batched-tokens
tant que la VRAM tient et que la latence reste
acceptable. Nous allons voir ça dans la section benchmarks.
Création d’un service systemd
Afin de faciliter le démarrage automatique de vLLM au boot du serveur, on va créer un service systemd. Mais le plus compliqué sera de le démarrer au bon moment, une fois que les GPU seront initialisés et prêts.
Voici un exemple de fichier de service systemd pour vLLM :
[Unit]Description=vLLM ServiceAfter=network-online.target nvidia-persistenced.serviceWants=network-online.targetStartLimitIntervalSec=300StartLimitBurst=3
[Service]Type=simpleUser=vllmuserGroup=vllmuserWorkingDirectory=/home/vllmuserEnvironment="PATH=/home/vllmuser/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"Environment="LD_LIBRARY_PATH=/usr/local/cuda/lib64:/usr/lib/x86_64-linux-gnu"Environment="CUDA_HOME=/usr/local/cuda"Environment="HF_HUB_OFFLINE=0"Environment="HF_HUB_CACHE=/home/vllmuser/.cache/huggingface"Environment="CUDA_VISIBLE_DEVICES=0,1"Environment="NO_COLOR=1"Environment="FORCE_COLOR=0"ExecStart=/home/vllmuser/venv/bin/vllm serve mistralai/Mistral-Small-3.2-24B-Instruct-2506 \ --distributed-executor-backend mp \ --tensor-parallel-size 2 --pipeline-parallel-size 1 \ --dtype auto \ --tokenizer-mode mistral --config-format mistral --load-format mistral \ --tool-call-parser mistral \ --gpu-memory-utilization 0.92 \ --max-model-len 4096 \ --max-num-seqs 64 \ --max-num-batched-tokens 8192 \ --swap-space 16 \ --port 8000 \ --host 0.0.0.0 \ --download-dir /home/vllmuser/.cache/huggingface
TimeoutStartSec=1800Restart=on-failureRestartSec=30KillMode=mixedKillSignal=SIGTERMStandardOutput=append:/var/log/vllm/vllm.logStandardError=append:/var/log/vllm/vllm-error.logSyslogIdentifier=vllm
# (Optionnel) des limites plus larges suivant ta charge# LimitNOFILE=1048576# TasksMax=infinity
[Install]WantedBy=multi-user.target
- Sauvegarde ce fichier sous
/etc/systemd/system/vllm.service
. - Remplace
vllmuser
par le nom de l’utilisateur qui exécute vLLM. - Recharge les services systemd :
sudo systemctl daemon-reload
- Préparation des répertoires de logs :
Avant d’activer le service, créez les répertoires et fichiers de logs avec les bonnes permissions :
# Création du répertoire de logssudo mkdir -p /var/log/vllm
# Création des fichiers de logssudo touch /var/log/vllm/vllm.logsudo touch /var/log/vllm/vllm-error.log
# Attribution des permissions à l'utilisateur vllmusersudo chown -R vllmuser:vllmuser /var/log/vllmsudo chmod 755 /var/log/vllmsudo chmod 644 /var/log/vllm/*.log
- Configuration de la rotation des logs :
Pour éviter que les logs ne prennent trop d’espace disque, configurez logrotate :
sudo tee /etc/logrotate.d/vllm << 'EOF'/var/log/vllm/*.log { daily missingok rotate 30 compress delaycompress notifempty copytruncate su vllmuser vllmuser}EOF
- Active le service pour qu’il démarre au boot :
sudo systemctl enable --now vllm.service
- Vérifie le statut du service :
sudo systemctl status vllm.service
Il doit indiquer que le service est actif (running). Si ce n’est pas le cas, consulte les logs avec :
sudo journalctl -u vllm.service -f
Benchmarks de contrôle des performances
Mettre en production un modèle comme Mistral-Small-3.2-24B ne consiste pas seulement à le faire tourner. Il est indispensable de mesurer ses performances pour vérifier qu’il répond à tes besoins en termes de latence, de débit (throughput) et de stabilité sous charge.
Pourquoi faire des benchmarks ?
Les benchmarks permettent de répondre à plusieurs questions clés :
- Quelle est la latence moyenne d’une réponse ?
- Combien de requêtes simultanées mon serveur peut-il gérer ?
- Quelle est la consommation des ressources GPU/CPU pendant l’inférence ?
- Quels paramètres de vLLM (
--max-model-len
,--max-num-seqs
,--max-num-batched-tokens
) offrent le meilleur compromis entre latence et débit ?
Sans ces mesures, il est impossible d’optimiser ton infrastructure et d’anticiper les coûts.
Mais la question que je me pose est : quel est le niveau de performance attendu en fonction des ressources à disposition (2× H100, 4× A100, etc.) et des paramètres choisis ?
Un moyen simple de mesurer la latence est d’utiliser un script Python qui envoie une requête à l’API vLLM :
import json, time, httpx
URL = "http://localhost:8000/v1/chat/completions"payload = { "model": "mistralai/Mistral-Small-3.2-24B-Instruct-2506", "messages": [{"role":"user","content":"Explique le RAG en 3 points"}], "stream": True, "tool_choice":"none", "temperature": 0, "top_p": 1, "max_tokens": 128}
with httpx.Client(timeout=120.0) as client: t0 = time.perf_counter() ttft = None tokens = 0 with client.stream("POST", URL, json=payload) as r: r.raise_for_status() for line in r.iter_lines(): if not line or not line.startswith("data:"): continue data = line[5:].strip() if data == "[DONE]": break obj = json.loads(data) delta = obj["choices"][0].get("delta", {}).get("content") if delta: tokens += len(delta.split()) if ttft is None: ttft = time.perf_counter() - t0 total = time.perf_counter() - t0
print(f"TTFT: {ttft:.3f}s | total: {total:.3f}s | tokens: {tokens} | decode rate: {tokens/(total-ttft):.1f} tok/s")
Ce script calcule :
- la latence totale d’une requête,
- le nombre de tokens générés,
- le débit en tokens par seconde.
En fonction des résultats, ajuste les paramètres suivants :
- Pour augmenter le contexte : élève
--max-model-len
(8192, 16384…), mais réduis--max-num-seqs
pour éviter l’OOM. - Pour maximiser le débit : garde
--max-model-len
bas (4096–8192), et augmente--max-num-seqs
/--max-num-batched-tokens
. - Pour réduire la latence : diminue
--max-num-seqs
afin que les batchs partent plus vite.
Conclusion
Avec vLLM, il est possible de déployer des modèles de langage de grande taille en production avec des performances optimales. En suivant les bonnes pratiques d’installation, de configuration et de benchmarking, tu peux t’assurer que ton infrastructure est prête à répondre aux exigences de tes applications.
N’hésite pas à expérimenter avec différents modèles et paramètres pour trouver la configuration qui convient le mieux à tes besoins spécifiques.