HomeRessources, Guides & Actualités – Actualités de l’intelligence artificielleIntelligence artificielleLa face cachée du cache KV : comment surmonter le vrai goulot d’étranglement des LLM

La face cachée du cache KV : comment surmonter le vrai goulot d’étranglement des LLM

On fait tourner un modèle de langage de pointe et, pendant les premières secondes, tout semble se dérouler à merveille. Les tokens défilent de manière fluide. Puis, sans crier gare, le flux s’interrompt, le débit s’effondre et la mémoire de la carte graphique s’emballe jusqu’à atteindre la zone rouge.

Le matériel n’a pas changé, les poids du modèle n’ont pas grossi d’un mégaoctet, et pourtant le système se met à ramer comme s’il tournait sur une machine vieille de dix ans.

On a souvent tendance à blâmer la complexité intrinsèque du « cerveau » de l’IA. Pourtant, le véritable problème réside ailleurs. Le facteur limitant de l’infrastructure d’IA moderne n’est pas le processeur graphique en soi, mais la façon dont le modèle stocke et récupère le contexte qu’il construit, jeton après jeton. C’est là tout l’enjeu du cache Key-Value (cache KV).

S’il s’avère indispensable pour accélérer la génération, il possède une face cachée particulièrement vorace en VRAM, capable de fragmenter la mémoire et de faire exploser les coûts d’infrastructure.

Pour déployer ces modèles à grande échelle ou simplement les exploiter sur une carte graphique grand public, il est crucial de comprendre l’origine de cette taxe mémoire.

Ce qu’il faut retenir de cette gestion mémoire

  • Le véritable goulet d’étranglement des modèles de langage modernes n’est pas la complexité de leur calcul brut, mais la gestion de la mémoire vive (VRAM).
  • Le cache Key-Value (cache KV) évite de recalculer tout l’historique de la conversation à chaque nouveau mot généré, mais il consomme une quantité massive de mémoire à mesure que le contexte s’allonge.
  • La fragmentation de la mémoire, qu’elle soit interne ou externe, peut gaspiller jusqu’à 80 % de l’espace alloué au cache KV.
  • Des techniques d’optimisation comme le Paged Attention, la quantification du cache en 8 bits (Q8) et le chunked pre-fill permettent de maximiser le débit et de faire tourner de grands modèles sur du matériel grand public.

Le compromis fondamental de l’inférence

Pour comprendre cette contrainte, il faut d’abord analyser le compromis fondamental qui s’opère lors de l’inférence d’un LLM. Une machine ne lit pas le texte comme nous ; elle traduit nos mots en vecteurs dans un espace à haute dimension. Prédire le mot suivant nécessite d’effectuer d’immenses multiplications de matrices. Ce processus se divise en deux étapes bien distinctes : le pre-fill (le pré-remplissage) et le decode (le décodage).

Durant la phase de pre-fill, le modèle traite l’intégralité de la requête initiale en une seule fois afin d’en construire une représentation mathématique. Cette étape dépend principalement de la puissance de calcul brute du GPU (on dit qu’elle est compute-bound). Mais dès que le premier token est généré, le système bascule dans la phase de décodage. À cet instant, la dynamique change du tout au tout : nous entrons dans un régime limité par la bande passante mémoire (memory-bound). Pour chaque nouveau mot produit, le modèle doit analyser tout ce qui a été écrit auparavant.

Sans optimisation, le système se retrouverait obligé de relire toute la conversation depuis le début à chaque nouveau caractère. Si la discussion compte déjà mille tokens, le calcul du millième imposerait de refaire toute l’analyse mathématique des neuf cent quatre-vingt-dix-neuf précédents. Pour s’épargner ce cauchemar computationnel à croissance quadratique (qui m’a d’ailleurs causé bien des migraines lors de mes premiers tests d’implémentation), nous utilisons le cache KV.

Nous extrayons les clés (Keys) et les valeurs (Values) — qui sont en quelque sorte les résumés mathématiques des tokens passés — et nous les stockons directement dans la VRAM pour ne plus avoir à les recalculer.

C’est un échange classique de mémoire contre du temps de calcul : on soulage le processeur, mais on paie un lourd tribut en espace de stockage.

La taille réelle du cache KV

Le premier écueil réside dans la taille de ce cache. Ce n’est pas un simple fichier statique temporaire, mais une structure de données dynamique qui gonfle à chaque mot généré. Sur des contextes très longs, cette mémoire de travail à court terme peut finir par occuper plus de place dans la carte graphique que les poids mêmes du modèle.

Prenons un exemple concret : l’exécution d’un modèle Llama de 13 milliards de paramètres (13B) sur une carte standard de 40 Go. Les poids du modèle à eux seuls requièrent environ 26 gigaoctets, soit 65 % de la mémoire disponible, avant même d’avoir traité la moindre requête utilisateur. Il ne reste donc que 35 % de la VRAM pour loger le cache KV de toutes les requêtes actives. Si une centaine d’utilisateurs se connectent simultanément, ces 35 % restants sont saturés instantanément. C’est à ce moment précis que le débit s’effondre. Lorsque l’espace de cache vient à manquer, le système doit soit s’arrêter, soit planter, soit recalculer les données qu’il était censé conserver, provoquant des latences immédiatement perceptibles à l’écran.

Mais alors, pourquoi ne pas tout mettre en cache, y compris les requêtes (les fameuses Queries) ? Après tout, on parle bien de matrices Q, K et V dans le mécanisme d’attention. C’est une question classique qui permet de distinguer rapidement ceux qui maîtrisent l’architecture mathématique sous-jacente des autres.

Si l’on observe la structure des matrices lors de la prédiction du token suivant, on s’aperçoit que seule la toute dernière ligne de la matrice de contexte est nécessaire. Pour l’obtenir, il faut disposer des matrices complètes des clés (Keys) et valeurs (Values) passées, mais le vecteur Query n’est requis que pour le nouveau token en cours de traitement. Les vecteurs Queries des anciens tokens ne seront plus jamais réutilisés pour les prédictions futures. Les stocker constituerait un pur gaspillage d’espace. Nous n’avons donc d’autre choix que de conserver cette imposante double matrice KV, qu’il faut gérer avec la plus grande rigueur.

La fragmentation : le problème de l’étage d’hôtel

Le second défi, plus pernicieux, concerne la gestion physique de cette mémoire et le phénomène de fragmentation. Les systèmes d’inférence traditionnels se montrent particulièrement inefficaces à ce jeu. Ne sachant pas à l’avance quelle sera la longueur de la réponse générée, ils adoptent une stratégie ultra-prudente en réservant d’emblée un bloc de mémoire contigu géant pour chaque requête, dimensionné sur la longueur maximale possible de la sortie.

On peut comparer cela à un hôtel où chaque client se verrait attribuer un étage entier dès son arrivée, sous prétexte qu’il pourrait éventuellement inviter des dizaines d’amis plus tard. Si la limite maximale de contexte est fixée à deux mille tokens, mais que l’utilisateur n’en génère que deux cents, 80 % de la mémoire réservée reste vide et totalement inaccessible pour d’autres requêtes. Cette fragmentation interne explique pourquoi 60 à 80 % de la mémoire dédiée au cache KV se retrouve régulièrement inutilisée.

La situation se complique encore avec la fragmentation externe. Même s’il reste techniquement assez d’espace global pour traiter une nouvelle demande, cette mémoire libre peut se retrouver dispersée en petits morceaux isolés entre d’autres requêtes actives.

Si le système réclame un unique bloc contigu de 500 tokens mais ne dispose que de dix espaces vides de 50 tokens chacun, il échouera à allouer la mémoire. Le matériel haute performance tourne alors à vide, incapable d’accueillir un nouvel utilisateur dans un espace pourtant théoriquement disponible.

Les solutions techniques pour optimiser la mémoire

Pour contourner ces limites, plusieurs techniques de gestion de la mémoire se sont imposées.

Le Paged Attention

Cette technique s’inspire directement du fonctionnement des systèmes d’exploitation en découpant le cache KV en blocs mémoire de taille fixe, généralement de 16 tokens. Au lieu d’exiger un grand espace contigu, ces pages peuvent être réparties n’importe où dans la VRAM. Une table de blocs associe ensuite ces adresses logiques aux emplacements physiques réels.

Cette approche élimine la fragmentation interne et externe, et permet à des moteurs d’inférence comme vLLM ou SGLang de traiter beaucoup plus de requêtes simultanées sur une même carte graphique.

La quantification du cache KV

Sur des configurations locales limitées à des cartes de 12 Go ou 16 Go, le cache KV peut rapidement saturer la VRAM restante. La quantification permet alors de réduire l’empreinte de ce cache.

De la même façon que l’on compresse les poids d’un modèle de 16 bits à 4 bits pour réduire sa taille globale, on peut quantifier les valeurs du cache KV. Passer à une précision de 8 bits (Q8) permet de diviser par deux l’espace requis par le cache, sans perte significative de qualité. Descendre plus bas est possible, mais cela commence à dégrader les capacités de raisonnement du modèle sur les conversations longues. C’est souvent l’unique méthode viable pour faire tourner des modèles de 30 ou 40 milliards de paramètres sur du matériel grand public.

Les leviers d’optimisation en production

Pour l’administration de serveurs de production, trois paramètres précis permettent d’ajuster le comportement du cache KV :

  1. Le taux d’utilisation de la mémoire GPU (GPU memory utilization) : Souvent configuré par défaut à 0,9, ce paramètre peut être poussé à 0,95 sur des charges de travail stables afin d’accueillir davantage d’utilisateurs simultanés.
  2. Le cache de préfixe (Prefix caching) : Particulièrement utile pour les pipelines de génération augmentée par récupération (RAG) ou les agents de programmation où chaque requête commence par une longue consigne système identique. Le système permet à différentes requêtes de pointer vers les mêmes blocs mémoire physiques contenant cette consigne partagée. On ne calcule et ne stocke cette partie qu’une seule fois, ce qui permet aux requêtes suivantes d’ignorer la phase de pre-fill et de réduire drastiquement le temps d’attente avant le premier token.
  3. Le pre-fill fragmenté (Chunked pre-fill) : Lorsqu’une nouvelle requête volumineuse arrive sur un serveur, elle monopolise généralement toutes les ressources pour sa phase de pre-fill, provoquant des micro-saccades sur les réponses en cours de génération pour les autres utilisateurs. Le chunked pre-fill découpe cette nouvelle requête en plusieurs morceaux, insérés dans les cycles de calcul restants entre deux étapes de décodage des autres sessions. Cette méthode permet d’améliorer le débit global jusqu’à 50 % en environnement de production.

Dans les scénarios exigeant une très faible latence, on peut également coupler ces techniques à du décodage spéculatif : un petit modèle d’ébauche propose plusieurs tokens d’un coup, que le grand modèle valide ensuite en une seule passe. Cela permet de maintenir le GPU actif pendant les temps d’attente liés aux accès mémoire.

Une question d’architecture et d’autonomie

Maîtriser ces aspects de la gestion du cache KV permet de faire des choix d’architecture éclairés. On se tournera par exemple vers vLLM pour optimiser le débit brut sur des modèles denses, ou vers SGLang pour gérer des architectures à mélange d’experts (MoE) comme DeepSeek.

En local, cela permet de décider de manière pragmatique s’il convient d’exécuter un modèle dense devant tenir entièrement en VRAM, ou un modèle MoE dont on pourra décharger les experts gourmands en mémoire vers la RAM système, tout en conservant les couches d’attention critiques sur le GPU.

Cette démarche s’inscrit dans une philosophie de réappropriation du matériel. Les fournisseurs d’infrastructure cloud tendent à imposer l’idée que l’exécution de modèles performants requiert nécessairement des grappes de serveurs massives et coûteuses.

Pourtant, une fois le cache KV configuré et optimisé par le biais du paged attention, de la quantification ou du déchargement mémoire, on réalise qu’une grande partie des tâches courantes peut être exécutée sur des configurations locales existantes, garantissant au passage la confidentialité des données traitées.

Leave a Reply

Your email address will not be published. Required fields are marked *