Quand on commence à faire tourner plusieurs modèles sur une grosse carte GPU, on tombe assez vite sur un problème un peu idiot : un modèle chargé par vLLM peut monopoliser plusieurs dizaines de gigaoctets de VRAM alors qu'il ne fait absolument rien.
Sur une carte de 24 Go, ce n'est déjà pas très amusant. Sur une carte de 96 Go utilisée pour faire tourner plusieurs LLM, ComfyUI et quelques autres joyeusetés, ça devient carrément frustrant. Vous avez une petite fortune vissée dans le serveur, mais une bonne partie de sa VRAM reste réservée par des modèles qui attendent tranquillement qu'on leur parle.
Heureusement, les versions récentes de vLLM disposent maintenant d'un vrai Sleep Mode. Et contrairement à ce qu'on pouvait lire dans certains anciens articles ou tickets GitHub, il ne s'agit plus d'une fonction expérimentale qu'il faut bricoler à coups de scripts CUDA douteux.
Le Sleep Mode de vLLM, ça sert à quoi ?
L'idée est simple : lorsqu'un modèle n'est pas utilisé, on peut demander à vLLM de libérer tout ou partie de la mémoire GPU qu'il utilise, sans pour autant arrêter complètement le serveur.
On peut ensuite réveiller le moteur lorsqu'une nouvelle requête doit être traitée.
Le principal intérêt n'est donc pas vraiment de faire faire une sieste au GPU pour économiser quelques watts. Le vrai avantage, c'est de récupérer de la VRAM afin qu'elle puisse être utilisée par un autre modèle, ComfyUI, un moteur d'inférence ou n'importe quel autre workload CUDA.
Et quand on dispose d'un GPU avec beaucoup de VRAM, c'est particulièrement intéressant : plutôt que de choisir trois modèles qui restent chargés 24 heures sur 24, on peut envisager d'en avoir davantage disponibles et ne garder réellement en VRAM que ceux qui travaillent.
Les différents niveaux de sommeil
vLLM propose plusieurs niveaux de sommeil. Les deux plus intéressants pour un serveur d'inférence classique sont les niveaux 1 et 2.
Level 1 : libérer la VRAM tout en gardant le modèle en RAM
Le niveau 1 est probablement celui qui nous intéresse le plus dans un environnement où plusieurs modèles doivent partager le même GPU.
Lorsque le moteur passe en sommeil de niveau 1, vLLM :
- décharge les poids du modèle de la VRAM vers la RAM CPU ;
- supprime le KV cache ;
- libère donc une grande partie de la mémoire GPU occupée par l'instance.
Les poids du modèle restent cependant disponibles en RAM. Au réveil, ils peuvent donc être retransférés vers le GPU sans avoir à relire entièrement le modèle depuis le stockage.
C'est le bon compromis lorsqu'on veut alterner rapidement entre plusieurs modèles.
Il faut évidemment disposer d'assez de RAM système pour accueillir les poids des modèles endormis. La VRAM ne disparaît pas par magie : dans ce mode, on la remplace essentiellement par de la RAM CPU.
Level 2 : sommeil profond
Le niveau 2 va plus loin. Cette fois, vLLM abandonne à la fois le KV cache et les poids du modèle au lieu de conserver une copie des poids en RAM CPU.
L'avantage est évident : on réduit également la pression sur la RAM du serveur.
Mais il y a une contrepartie : le réveil est plus complexe. Les poids ayant été abandonnés, il faut les recharger avant de pouvoir recommencer l'inférence.
Ce mode est particulièrement adapté à certains workflows d'entraînement ou de RLHF, ou lorsqu'on souhaite remplacer les poids du modèle. Pour simplement mettre un LLM de côté quelques minutes avant de le rappeler, le niveau 1 est généralement beaucoup plus pratique.
Activer le Sleep Mode avec vLLM
Le Sleep Mode n'est pas activé automatiquement. Avec vllm serve, il faut lancer le moteur avec l'option :
--enable-sleep-mode
Pour utiliser les endpoints HTTP de contrôle, le serveur doit également être lancé avec :
VLLM_SERVER_DEV_MODE=1
Par exemple :
VLLM_SERVER_DEV_MODE=1 vllm serve mistralai/Mistral-7B-Instruct-v0.3 \
--enable-sleep-mode \
--port 8000
Attention toutefois : le mode VLLM_SERVER_DEV_MODE expose des endpoints d'administration qui ne doivent pas être rendus accessibles publiquement. Ce n'est clairement pas une API à mettre directement derrière Internet en espérant que personne ne trouve le bouton Sleep.
Mettre le modèle en sommeil
Une fois le serveur lancé, le fonctionnement est particulièrement simple.
Pour envoyer le modèle en sommeil de niveau 1 :
curl -X POST http://localhost:8000/sleep?level=1
On peut ensuite vérifier son état :
curl http://localhost:8000/is_sleeping
Le serveur retourne alors :
{"is_sleeping":true}
Et pour le réveiller :
curl -X POST http://localhost:8000/wake_up
Une nouvelle interrogation :
curl http://localhost:8000/is_sleeping
donnera cette fois :
{"is_sleeping":false}
Et le moteur peut à nouveau recevoir des requêtes d'inférence.
Et le niveau 2 ?
Le sommeil profond fonctionne de la même manière au départ :
curl -X POST http://localhost:8000/sleep?level=2
Mais le réveil est différent, puisque les poids du modèle ont été abandonnés.
vLLM permet notamment de réallouer séparément la mémoire destinée aux poids et celle destinée au KV cache :
curl -X POST "http://localhost:8000/wake_up?tags=weights"
Les poids doivent ensuite être rechargés, puis le KV cache peut être réalloué :
curl -X POST http://localhost:8000/collective_rpc \
-H "Content-Type: application/json" \
-d '{"method":"reload_weights"}'
curl -X POST "http://localhost:8000/wake_up?tags=kv_cache"
On comprend vite pourquoi le niveau 1 est beaucoup plus intéressant pour un simple serveur d'inférence multi-modèles.
Ce n'est pas exactement un système d'hibernation automatique
Il faut quand même apporter une nuance importante : activer --enable-sleep-mode ne signifie pas que vLLM va automatiquement regarder sa montre et mettre le modèle au lit après cinq minutes d'inactivité.
Le Sleep Mode fournit le mécanisme. C'est à vous, ou à votre infrastructure, de décider quand appeler /sleep et quand appeler /wake_up.
On peut donc facilement imaginer un petit proxy devant plusieurs instances de vLLM :
- un modèle inutilisé depuis quelques minutes passe en
sleep level 1; - sa VRAM devient disponible pour une autre instance ;
- une nouvelle requête arrive pour ce modèle ;
- le proxy appelle
/wake_up; - le modèle revient en VRAM ;
- la requête est ensuite envoyée au moteur.
Autrement dit, vLLM fournit maintenant pratiquement toutes les briques nécessaires pour construire une sorte de pool dynamique de modèles partageant un même GPU.
Le vrai intérêt : faire tourner plus de modèles sur le même GPU
C'est probablement là que le Sleep Mode devient vraiment intéressant.
Imaginons un GPU disposant de 96 Go de VRAM avec plusieurs services :
- un premier LLM utilisant environ 35 Go ;
- un second LLM utilisant environ 25 Go ;
- ComfyUI utilisant ponctuellement plusieurs dizaines de gigaoctets ;
- deux autres LLM que l'on aimerait rendre disponibles.
Sans mécanisme de déchargement, on arrive rapidement à la limite de VRAM alors même que la majorité des modèles ne travaillent pas simultanément.
Avec le Sleep Mode, on peut envisager quelque chose de beaucoup plus souple : les modèles actifs restent en VRAM, tandis que les autres dorment en RAM CPU et sont remontés sur le GPU à la demande.
Évidemment, ce fonctionnement introduit une latence au premier appel après un réveil. Copier plusieurs dizaines de gigaoctets de RAM vers la VRAM n'est pas instantané. Mais cela reste potentiellement beaucoup plus rapide que de tuer complètement le processus vLLM puis de recharger le modèle depuis le SSD.
Attention aux requêtes en cours
Il ne faut pas non plus envoyer sauvagement un moteur dormir pendant qu'il est en train de générer une réponse.
La documentation de vLLM précise que le moteur ne doit pas traiter de requêtes pendant sa période de sommeil. Les versions récentes exposent d'ailleurs plusieurs comportements de pause permettant de choisir comment gérer les requêtes existantes.
Dans une architecture automatisée, il faudra donc prendre en compte les requêtes en cours avant de décharger un modèle. Un timeout d'inactivité géré par un proxy ou un orchestrateur est une approche beaucoup plus propre qu'un cron qui balance un /sleep toutes les cinq minutes avec la finesse d'un parpaing.
CUDA, ROCm et allocation mémoire
Le Sleep Mode est aujourd'hui supporté notamment sur les plateformes CUDA et ROCm. Lorsqu'il est activé, vLLM utilise son allocateur mémoire adapté à ce fonctionnement afin de pouvoir restituer efficacement la mémoire GPU.
C'est un point important : faire simplement un torch.cuda.empty_cache() n'aurait pas le même effet. Tant que les poids du modèle sont référencés et utilisés par le moteur, leur VRAM ne peut évidemment pas être récupérée par un autre processus.
Le Sleep Mode agit beaucoup plus profondément puisqu'il fait partie du fonctionnement même du moteur vLLM.
Alors, faut-il encore chercher une alternative à vLLM ?
Pour cette problématique précise, beaucoup moins qu'avant.
transformers, TensorRT-LLM, DeepSpeed et d'autres solutions restent évidemment intéressantes selon les workloads. Mais remplacer vLLM uniquement parce qu'on souhaite pouvoir libérer la VRAM entre deux utilisations n'est plus vraiment justifié.
vLLM conserve ses points forts habituels — continuous batching, gestion efficace du KV cache, API compatible OpenAI et très bonnes performances d'inférence — tout en disposant maintenant d'un vrai mécanisme permettant de mettre les moteurs en sommeil.
Conclusion
Le Sleep Mode de vLLM change finalement assez fortement la manière dont on peut penser un serveur GPU multi-modèles.
Plutôt que de raisonner uniquement en disant « mon GPU fait 96 Go, donc je peux charger trois modèles de 30 Go », on peut commencer à raisonner comme avec une ressource partagée : certains modèles sont actifs, les autres sont temporairement déchargés et reviennent lorsqu'on en a besoin.
Le level 1 est particulièrement intéressant pour ce type d'utilisation : les poids quittent la VRAM mais restent en RAM, le KV cache est supprimé, et un simple appel à /wake_up permet de remettre le moteur en service.
Ce n'est pas encore Kubernetes pour LLM dans une seule carte graphique, mais on commence franchement à s'en rapprocher.
Et surtout, votre GPU à plusieurs milliers d'euros n'est plus obligé de garder 30 Go de VRAM réservés à un modèle simplement parce que quelqu'un pourrait, éventuellement, lui poser une question dans trois heures.