Un GPU de 8 Go est trop faible pour faire tourner trois modèles d'IA ? Faux. Voici l'astuce C++ qui permet de tout faire tenir sur une carte graphique ancienne.

0.00.592.688 E ggmlbackendcudabuffertypeallocbuffer : allocation de 1536,00 MiB sur l'appareil 0 échouée : cudaMalloc a manqué de mémoire.
0.00.593.132 E llamainitfrom_model : échec d'initialisation du contexte : impossible d'allouer le buffer pour le cache KV.

UNE CATASTROPHE QUI PARLE À TOUT LE MONDE

Vous avez trois agents IA qui tournent chacun avec un modèle différent : un pour générer du code, un pour vérifier la sécurité, et un pour rédiger la documentation. Tout doit se faire en temps réel, sans latence. Problème : votre carte graphique est une antiquité, une NVIDIA GTX 1080 avec seulement 8 Go de VRAM. Impossible d'ajouter un deuxième modèle sans voir le troisième s'écrouler avec une erreur cudaMalloc failed: out of memory.

L'erreur est humaine, mais dans ce cas précis, elle vient d'un système conçu pour un seul utilisateur à la fois. Chaque modèle essaie de réserver l'intégralité du cache KV dès le démarrage, avant même d'avoir généré un seul token. Résultat : le premier modèle prend 6,5 Go sur 8, et les deux suivants n'ont plus rien. Le cache KV, c'est comme une place de parking réservée pour chaque mot généré. Si vous réservez 172 032 places d'un coup, même sur un petit modèle, ça fait mal.

POURQUOI LES TUTORIELS VOUS MENTENT (GENTIMENT)

Les tutoriels classiques vous disent : « Lancez trois processus séparés, ça devrait marcher ». Sauf que personne ne précise que chaque processus essaie de s'emparer de la mémoire vidéo comme un enfant dans un magasin de bonbons. Le premier modèle prend tout, les autres n'ont plus leur chance. Le système n'a pas de file d'attente, pas de comptabilité partagée. C'est comme si trois personnes essayaient de garer leur voiture sur une seule place : deux vont se faire klaxonner.

L'erreur n'est pas dans l'algorithme, mais dans l'absence de gestion des ressources. Il manque un comptable qui dit : « Non, tu ne peux pas réserver ça maintenant, il n'y a plus de place ». C'est exactement ce que propose l'outil lmxd, un démon C++ qui prend le contrôle de la carte graphique et décide qui a le Droit de l'utiliser.

LE COMPTABLE QUI SAUVE VOTRE GPU

Le cœur du système s'appelle le VRAM Ledger. Son rôle ? Vérifier que la somme de la mémoire déjà utilisée + la nouvelle réservation ne dépasse pas 90 % de la capacité totale. La règle est simple : currentlyused + newestimate ≤ 90 % cap.

Voici le code qui fait tout le travail, en quelques lignes seulement :

bool VramLedger::tryreserve(uint64t modeltablebytes) {
  // Une section critique pour éviter les conflits entre processus parallèles
  std::lock_guard lock(mu_);
  if (.initialized_) {
    return false;
  }
  uint64_t projected = 0;
  if (.addu64(allocatedbytes, modeltable_bytes, &projected)) {
    return false;
  }
  if (projected > maxvrambytes_) {
    return false;
  }
  allocatedbytes = projected;
  return true;
}

Le mutex est crucial : sans lui, deux processus pourraient passer la vérification en même temps et réserver plus que ce qui est disponible. Personne n'a envie de déboguer une course à la mémoire, surtout à 2 heures du matin.

LA BONNE ORDRE DES CHOSES : D'ABORD VÉRIFIER, ENSUITE CHARGER

L'erreur classique ? Charger le modèle d'abord, puis vérifier la mémoire. Résultat : vous avez déjà lu 1 Go de données sur le disque pour un modèle que vous allez refuser. Pire : vous avez lancé une allocation cudaMalloc qui va échouer, mais trop tard pour annuler le chargement. Book before you build, disent les développeurs : réserve d'abord, construis ensuite.

Le gestionnaire de modèles dans lmxd fait exactement ça. Il initialise le backend CUDA une seule fois pour toute la carte, et réutilise les modèles déjà chargés si deux agents demandent la même version. Plus besoin de gaspiller de la mémoire avec trois processus séparés.

bool LlamaSingleService::acquiremodel(const std::string& path, std::string* errout) {
  // Protège chaque point d'entrée pour éviter les conflits
  std::lock_guard lock(mu_);

  if (.backendinited) {
    // Initialise les backends CPU/GPU une seule fois
    llamabackendinit();
    backendinited = true;
  }

  const auto it = models_.find(path);
  if (it .= models_.end()) {
    // Réutilise un modèle déjà chargé et incrémente le compteur
    it->second.refcount += 1;
    return true;
  }

  // Charge un nouveau modèle si nécessaire
  llamamodelparams params = llamamodeldefault_params();
  llamamodel* model = llamamodelloadfromfile(path.cstr(), params);
  if (model == nullptr) {
    if (err_out .= nullptr) {
      *errout = "llamamodelloadfrom_file a échoué pour le chemin : " + path;
    }
    return false;
  }

  ModelSlot slot{};
  slot.model = model;
  slot.refcount = 1;
  models_.emplace(path, std::move(slot));
  return true;
}

LA DÉMONSTRATION EN PRATIQUE

Pour tester, rien de plus simple. On commence par trois modèles légers mais puissants : SmolLM2-360M-Instruct-Q4KM, Qwen2-0.5B-Instruct-Q4KM et Llama-3.2-1B-Instruct-Q4KM. Ensemble, ils pèsent environ 1,4 Go sur le disque. Sur une GTX 1080 avec 8 Go de VRAM, la mémoire utilisée avant tout lancement ? 22 Mo.

Première tentative avec la méthode classique : on lance le premier modèle, et hop, 6,5 Go sont réservés immédiatement. Le deuxième modèle essaie de s'ajouter : cudaMalloc échoue, le processus meurt. Le troisième aussi. Résultat : un seul modèle tourne, les deux autres laissent des logs d'erreur.

Avec lmxd, c'est différent. On lance le démon avec une table qui associe chaque modèle à sa taille sur disque. On utilise l'option --admission-percent 90 pour limiter l'utilisation à 90 % de la mémoire. Puis on enregistre les trois modèles via une socket Unix :

./build/src/lmxd --socket /tmp/lmxd.sock --vram-table configs/model_vram.example.txt &
nc -U /tmp/lmxd.sock <

Trois modèles différents, trois identifiants d'agents, 1,58 Go réservés contre une limite de 7,73 Go sur la même carte qui n'en acceptait qu'un seul auparavant.

COMMENT ÇA MARCHE SANS S'EFFONDRER

Le secret ? La mémoire vidéo n'est pas gaspillée. Quand un agent n'est pas actif, son cache KV est déplacé vers la RAM de l'ordinateur. Il ne consomme plus de VRAM. Résultat : à l'état stable, avec deux modèles enregistrés, seulement 926 Mo sont utilisés sur la carte. Le démon lmxd est le seul processus CUDA en cours d'exécution.

Quand on bascule d'un agent à l'autre, le système fait ce qu'on appelle un KV-swap : il évacue le cache de l'agent inactif vers la RAM, puis ramène celui de l'agent actif. Tout ça en moins de 440 ms sur la même GTX 1080. La réponse du démon indique exactement ce qui s'est passé :

OK schema=lmx-daemon/1
OK invoke=DECODE agent_id=smol
OK prompttokens=4 generatedtokens=24 stoppedoneos=false
OK elapsed_ms=125.495
OK kvswapevicted=qwen kvswaprestored=true
BEGIN_RESPONSE
[Weather]. I'm looking forward to seeing you all. I'm [Your Name] and I'm a [Your
END_RESPONSE

La ligne kvswaprestored=true signifie que l'agent que vous venez de solliciter avait été suspendu, pas tué. Son état était toujours disponible en RAM, prêt à être rechargé en quelques millisecondes.

UNE SOLUTION QUI VIENT DE LA 5G

L'auteur de ce système n'est pas un expert en GPU. Il vient du monde des télécommunications, plus précisément de la 5G et de la recherche sur la 6G. Pour lui, gérer des ressources limitées comme la mémoire vidéo, c'est comme gérer des connexions sur un réseau mobile : Connection Admission Control.

Sur un téléphone, quand vous lancez un appel, la station de base ne dit pas « Oui, tu peux parler ». Elle vérifie d'abord si elle a assez de ressources. Si non, elle refuse la connexion. C'est exactement ce que fait lmxd avec la VRAM : il contrôle l'admission avant de permettre à un modèle de s'exécuter.

COMMENT INSTALLER ET UTILISER LMXD

Pour reproduire cette magie, il suffit de cloner le dépôt et de compiler le démon :

git clone  && cd 
cmake -S . -B build -DCMAKEBUILDTYPE=Release -DLMXWITHLLAMA_CUDA=ON
cmake --build build -j"$(nproc)"

# Démo de superposition (sans modèle requis) :
./build/src/layerstreamdemo

# Lancement du démon :
./build/src/lmxd --socket /tmp/lmxd.sock --vram-table configs/model_vram.example.txt &

# Enregistrement des modèles :
nc -U /tmp/lmxd.sock <

Le démon écoute sur une socket Unix. Vous pouvez lui envoyer des commandes simples : REGISTER pour ajouter un modèle, DECODE pour générer du texte avec un agent spécifique, LIST pour voir les modèles disponibles, UNREGISTER pour libérer un modèle.

LES PERFORMANCES : CE QU'IL FAUT RETENIR

Sur une GTX 1080, avec trois modèles légers, le démon permet de faire tourner les trois agents en parallèle sans crash. La mémoire utilisée reste sous les 80 % de la capacité totale, ce qui évite les erreurs de type VRAMLEDGERDENY.

Quand un agent n'est pas actif, il ne consomme presque rien en VRAM. Le système de KV-swap est transparent : vous ne voyez que le résultat final, pas les échanges de mémoire entre la carte graphique et la RAM.

Le démon ne rend pas les calculs plus rapides, mais il permet à trois modèles de tourner là où la méthode classique n'en fait tourner qu'un seul.

POURQUOI CELA VA CHANGER VOTRE QUOTIDIEN

Si vous avez un vieux PC avec une carte graphique limitée, ce système vous permet de faire tourner plusieurs agents IA en parallèle sans acheter du matériel neuf. Plus besoin de choisir entre un modèle pour le code, un autre pour la documentation, et un troisième pour les recherches. Tout peut tourner en même temps, même sur une carte graphique ancienne.

C'est une solution technique, mais elle a un impact concret : vous économisez de l'argent, vous évitez d'acheter du matériel coûteux, et vous pouvez enfin exploiter pleinement le potentiel de vos agents IA.

LES LIMITES À CONNAÎTRE

Le système a des limites. Il ne permet pas de faire tourner trois modèles en parallèle en temps réel avec des contextes très longs. La mémoire vidéo reste un goulot d'étranglement. Mais pour des tâches légères et des modèles petits, c'est une solution viable.

De plus, le démon lmxd n'est pas une solution magique. Il faut configurer correctement la table de VRAM et respecter les limites de mémoire. Si vous dépassez le seuil de 90 %, le système refusera simplement l'ajout d'un nouveau modèle.

LE FUTUR : VERS UNE MEILLEURE GESTION DES RESSOURCES

Ce système montre que la gestion des ressources en IA est souvent négligée. Pourtant, elle est cruciale pour permettre à plusieurs modèles de coexister sur une même carte graphique. À l'avenir, on peut imaginer que les frameworks comme llama.cpp intègrent nativement ce type de comptabilité pour éviter les erreurs de mémoire.

En attendant, lmxd est une solution open source qui permet de contourner les limites actuelles et de faire tourner plusieurs agents IA sur un seul GPU ancien. Une solution simple, mais efficace.

COMMENT PARTICIPER AU PROJET

Le projet est open source. Vous pouvez contribuer en testant le démon, en rapportant des bugs, ou en améliorant le code. Le dépôt est disponible à la fin de l'article (lien non inclus dans le texte source, donc non reproduit ici).

Si vous avez un vieux GPU qui traîne dans un placard, c'est peut-être le moment de lui donner une seconde vie avec lmxd.

EN RÉSUMÉ : CE QU'IL FAUT RETENIR

Un GPU de 8 Go peut faire tourner trois modèles d'IA en parallèle si on utilise un système de gestion des ressources comme lmxd.

Le système repose sur trois piliers :

  • Un VRAM Ledger qui vérifie la mémoire disponible avant d'autoriser une réservation.
  • Un ordre strict : vérifier avant de charger.
  • Un backend CUDA unique pour éviter le gaspillage de mémoire.

Résultat : trois agents IA tournent en parallèle sur une carte graphique ancienne, sans erreur de mémoire.

La solution n'est pas révolutionnaire, mais elle est efficace : c'est de la bonne vieille gestion des ressources, appliquée à l'IA.

Sources :
  • Towards Data Science

L'indépendance de CLODCO est votre garantie.

Pour que l'actualité de l'IA reste sans filtre et sans concession, votre soutien est indispensable. Votre contribution est le seul moteur de notre liberté éditoriale.

Soutenir CLODCO