Polydesk-logotype
Polydesk.ai — Header

Memory Management LLM

La gestion mémoire des LLM (Large Language Models) regroupe les techniques d’allocation, d’optimisation et de libération de la mémoire GPU (VRAM) utilisée pour stocker les poids du modèle, le KV-cache, les activations et les overhead système pendant l’inférence et l’entraînement.

Memory Management LLM en bref
Catégorie
Infrastructure IA / Optimisation GPU
4 composantes de la mémoire GPU
Poids du modèle (fixe), KV-cache (variable), activations (transitoire), overhead système (~0,5-2 Go)
Formule poids
VRAM poids = Nb paramètres × Octets par paramètre
Goulot d’étranglement
Le KV-cache dépasse souvent les poids du modèle pour les longs contextes
Techniques clés
Quantification, PagedAttention, FlashAttention, parallélisme tensor/pipeline, offloading
Outils
vLLM, TensorRT-LLM, SGLang, llama.cpp, Ollama

Les 4 composantes de la mémoire GPU d’un LLM

Comprendre comment la VRAM est consommée est le prérequis pour l’optimiser. Chaque requête d’inférence mobilise quatre catégories de mémoire distinctes.

1. Poids du modèle

Les paramètres appris du réseau de neurones. C’est la composante fixe : les poids ne changent pas entre les requêtes (sauf en fine-tuning). La mémoire requise est directement proportionnelle au nombre de paramètres et à la précision de stockage.

Modèle Paramètres FP32 (4 octets) FP16/BF16 (2 octets) INT8 (1 octet) INT4 (0,5 octet)
Llama 3.1 8B 8 Mds 32 Go 16 Go 8 Go 4 Go
Mistral 7B 7 Mds 28 Go 14 Go 7 Go 3,5 Go
Llama 3.1 70B 70 Mds 280 Go 140 Go 70 Go 35 Go
Qwen 3 32B 32 Mds 128 Go 64 Go 32 Go 16 Go
Llama 3.1 405B 405 Mds 1 620 Go 810 Go 405 Go ~203 Go

En production, quasiment personne n’utilise FP32 pour l’inférence. FP16/BF16 est le standard, et la quantification INT4/INT8 est devenue la norme pour les déploiements à mémoire contrainte. Un modèle 70B en INT4 (Q4_K_M) tient dans ~35 Go, soit un seul GPU H100 80 Go avec de la marge pour le KV-cache.

2. KV-cache

Le « monstre caché » de la mémoire LLM. Le KV-cache stocke les vecteurs Key et Value de chaque token traité, pour chaque couche du transformer. Il croît linéairement avec la longueur du contexte et le nombre de requêtes concurrentes.

# Formule de taille du KV-cache
KV_cache = 2 × nb_couches × nb_têtes_KV × dim_tête × nb_tokens × octets_par_valeur

# Llama 3.1 70B avec GQA (8 têtes KV), contexte 128K, BF16
# 2 × 80 × 8 × 128 × 131072 × 2 = ~34 Go par requête

# 4 requêtes concurrentes à 128K tokens :
# 4 × 34 Go = 136 Go de KV-cache seul

Pour un modèle 70B avec un contexte de 128K tokens, le KV-cache par requête (~34 Go) est comparable aux poids du modèle en INT4 (~35 Go). Avec 4 requêtes concurrentes, le KV-cache consomme plus de mémoire que le modèle entier. C’est pourquoi les techniques de KV-cache optimization (PagedAttention, quantification KV, éviction) sont critiques.

Les architectures modernes comme GQA (Grouped-Query Attention, utilisée par Llama 3, Mistral, Qwen) réduisent le KV-cache de 4 à 8× en partageant les têtes KV entre plusieurs têtes de requête. Sans GQA, le cache de Llama 3.1 70B à 128K serait de ~272 Go par requête au lieu de 34 Go.

Le KV-cache est le vrai goulot d’étranglement, pas les poids Beaucoup de développeurs calculent la VRAM nécessaire en regardant uniquement la taille des poids du modèle. C’est une erreur. En production avec des batchs de requêtes et des contextes longs, le KV-cache consomme souvent plus de mémoire que les poids. Toujours calculer : VRAM totale = poids + KV-cache × batch_size + activations + overhead. L’erreur ‘OOM’ (Out of Memory) la plus courante survient au runtime quand le KV-cache sature la mémoire restante après le chargement du modèle.

3. Activations

Les sorties intermédiaires calculées pendant le forward pass (entre les couches). En inférence, les activations sont modestes (5-10 % de la mémoire totale pour des batchs standards) car seules les activations de la couche courante sont nécessaires. En entraînement, les activations sont beaucoup plus volumineuses car elles doivent être conservées pour la rétropropagation (gradient checkpointing peut les réduire au prix d’un recalcul).

4. Overhead système

Le contexte CUDA, les allocateurs mémoire PyTorch, les kernels du framework d’inférence (vLLM, TensorRT-LLM), et les buffers de workspace consomment 0,5 à 2 Go de VRAM avant même le chargement du modèle. La fragmentation mémoire peut gaspiller 20-30 % de VRAM supplémentaire sans optimisation (PagedAttention réduit ce gaspillage à < 4 %).

Calculer la VRAM nécessaire

# Formule complète de VRAM pour l'inférence
VRAM_totale = (
    poids_modele                          # Paramètres × octets/param
    + kv_cache_par_requete × batch_size   # 2 × layers × kv_heads × dim × seq_len × bytes
    + activations                         # ~5-10% du total (inférence)
    + overhead_systeme                    # 0.5-2 Go (CUDA, framework)
)

# Exemple : Llama 3.1 70B en INT4, batch 8, contexte 8K
poids = 70e9 * 0.5 / 1e9                  # 35 Go (INT4)
kv_cache = 2 * 80 * 8 * 128 * 8192 * 2 / 1e9  # ~2.1 Go par requête (BF16)
kv_total = 2.1 * 8                        # 16.8 Go pour 8 requêtes
activations = 3                           # ~3 Go estimé
overhead = 1.5                            # ~1.5 Go

total = 35 + 16.8 + 3 + 1.5              # ~56.3 Go → 1× H100 80 Go suffit

# Même modèle, contexte 128K, batch 4
kv_cache_long = 2 * 80 * 8 * 128 * 131072 * 2 / 1e9  # ~34 Go par requête
kv_total_long = 34 * 4                    # 136 Go → dépasse 1× H100
total_long = 35 + 136 + 3 + 1.5          # ~175.5 Go → nécessite 4× A100 ou 2× H200
Règle empirique rapide Pour l’inférence d’un modèle en FP16 : VRAM minimale ≈ 2× le nombre de paramètres en milliards (en Go). Un modèle 7B nécessite ~14 Go, un 70B ~140 Go. En INT4 (Q4_K_M), divisez par 4 : 7B → ~4 Go, 70B → ~35 Go. Ajoutez 20-50 % pour le KV-cache et l’overhead selon votre batch size et longueur de contexte. Pour le fine-tuning complet en FP16, multipliez par 4-6× (gradients, optimiseur, activations).

Techniques d’optimisation mémoire

Quantification des poids

Réduire la précision des poids du modèle (FP16 → INT8 → INT4) est le levier le plus direct pour économiser de la VRAM. Les méthodes dominantes :

GPTQ : quantification post-entraînement utilisant une calibration sur un petit dataset. Produit des modèles en INT4/INT8 avec une perte de qualité minimale. Largement supporté (vLLM, llama.cpp, Hugging Face).

AWQ (Activation-Aware Weight Quantization) : identifie les poids les plus sensibles (ceux qui produisent de grandes activations) et les protège de la quantification agressive. Meilleure qualité que GPTQ pour les taux de compression élevés.

bitsandbytes NF4 : format 4 bits de Hugging Face, optimisé pour les distributions gaussiennes des poids. Réduction de ~62 % de la VRAM avec ~2 % de dégradation de perplexité. Le standard pour le fine-tuning avec QLoRA.

NVFP4 : format 4 bits de NVIDIA pour les GPU Blackwell, avec des facteurs d’échelle FP8 par bloc pour une meilleure précision que NF4. < 1 % de perte de qualité sur les benchmarks majeurs.

Quantification du KV-cache

Distincte de la quantification des poids. Réduire la précision des vecteurs K et V dans le cache (FP16 → FP8 → NVFP4) réduit directement la taille du cache, le goulot d’étranglement principal pour les longs contextes. Supporté nativement par vLLM et TensorRT-LLM sur H100+. Les deux techniques se cumulent (poids en INT4 + KV-cache en FP8).

PagedAttention (vLLM)

Gère le KV-cache en pages de taille fixe, allouées dynamiquement. Élimine la fragmentation mémoire (de 60-80 % de gaspillage à < 4 %) et permet le partage de pages entre requêtes avec des préfixes communs (prefix caching). Throughput 2-4× supérieur sur le même matériel. C’est la technique d’optimisation mémoire la plus impactante pour l’inférence en production.

FlashAttention

Optimise les accès mémoire pendant le calcul d’attention en travaillant par blocs dans la SRAM (mémoire ultra-rapide on-chip du GPU) au lieu de tout stocker en HBM (VRAM). Réduit les lectures/écritures mémoire et accélère l’attention de 2-4×. Ne réduit pas directement la taille du KV-cache mais accélère son utilisation. Complémentaire à PagedAttention.

Parallélisme GPU

Quand un modèle ne tient pas dans un seul GPU, on le distribue sur plusieurs GPU.

Tensor Parallelism (TP) : chaque couche du modèle est découpée et distribuée entre les GPU. Les GPU communiquent à chaque couche via NVLink (haute bande passante, basse latence). Réduit la mémoire par GPU proportionnellement au nombre de GPU. Idéal pour les modèles 70B+ sur 2-8 GPU dans un même nœud.

Pipeline Parallelism (PP) : chaque GPU reçoit un ensemble de couches consécutives. Les GPU communiquent uniquement entre les frontières de stages. Moins de communication que TP, mais introduit des « bulles » de pipeline (temps d’inactivité). Idéal pour les modèles 500B+ sur des clusters multi-nœuds.

Expert Parallelism (EP) : pour les modèles Mixture-of-Experts (MoE), chaque GPU héberge un sous-ensemble d’experts. Seuls les experts activés par la requête sont chargés, ce qui réduit drastiquement la mémoire effective par requête.

Offloading GPU → CPU → SSD

Quand la VRAM est insuffisante, les composantes les moins actives sont déplacées vers des niveaux de stockage plus lents mais plus grands. Les poids rarement accédés peuvent être streamés depuis la RAM CPU. Le KV-cache des sessions inactives peut être offloadé vers la RAM ou le SSD (via LMCache). Ollama et llama.cpp supportent l’offloading de couches vers le CPU (le paramètre num_gpu contrôle le nombre de couches sur GPU).

Le compromis est toujours le même : la latence augmente car la RAM CPU est 30-60× plus lente que la HBM GPU, et le SSD est encore plus lent. L’offloading est un dernier recours quand les autres optimisations ne suffisent pas.

Inférence vs entraînement : mémoires différentes

Composante mémoire Inférence Entraînement (fine-tuning complet) Fine-tuning LoRA
Poids du modèle 1× (FP16) ou 0,25-0,5× (quantifié) 1× (FP16 ou BF16) 1× (FP16, gelé) + ~1-5 % adaptateurs
KV-cache Variable (proportionnel au contexte × batch) N/A (pas de KV-cache en training) N/A
Gradients N/A 1× taille des poids (FP16) ~1-5 % (gradients des adaptateurs seuls)
États optimiseur N/A 2× taille des poids (AdamW : momentum + variance) ~2-10 % des poids
Activations 5-10 % du total 30-50 % du total (sans gradient checkpointing) Réduit (moins de couches actives)
VRAM totale typique 1-2× taille poids (FP16) + KV-cache 4-6× taille poids (FP16) 1,2-1,5× taille poids (FP16)

Le fine-tuning complet d’un modèle 70B en FP16 nécessite environ 4-6× la taille des poids (poids + gradients + optimiseur + activations), soit 560-840 Go. C’est pourquoi LoRA/QLoRA est devenu le standard : en ne fine-tunant que 1-5 % des paramètres (les adaptateurs low-rank), la VRAM nécessaire tombe à 1,2-1,5× la taille des poids, soit ~100-105 Go pour un 70B en BF16 avec QLoRA (poids en NF4 + adaptateurs en BF16).

Configurations GPU recommandées

Workload Modèle Configuration minimum Configuration recommandée
Inférence locale (dev) 7-8B Q4 1× RTX 4060 8 Go 1× RTX 4070 Ti 16 Go
Inférence locale (avancé) 32B Q4 1× RTX 4090 24 Go 1× RTX 5090 32 Go
Inférence production 70B INT4, batch 8, ctx 8K H100 80 Go 2× H100 80 Go (TP=2)
Inférence long contexte 70B BF16, ctx 128K A100 80 Go (TP=4) H200 141 Go (TP=2)
Fine-tuning LoRA 70B QLoRA (NF4) 2× A100 80 Go 2× H100 80 Go
Fine-tuning complet 70B BF16 8× A100 80 Go 8× H100 80 Go

Memory-bound vs compute-bound

L’inférence LLM est souvent « memory-bound » (limitée par la bande passante mémoire, pas par la puissance de calcul). Le GPU est capable de calculer beaucoup plus vite qu’il ne peut lire les données depuis la VRAM. C’est pourquoi la bande passante HBM (3,35 To/s sur H100, 4,8 To/s sur H200) est aussi importante que le nombre de TFLOPS.

La phase de prefill (traitement du prompt) est compute-bound (beaucoup de tokens à traiter en parallèle, le GPU est occupé). La phase de decode (génération token par token) est memory-bound (chaque token nécessite de lire tous les poids du modèle depuis la VRAM). C’est pourquoi la vitesse de génération (tokens/seconde) est directement proportionnelle à la bande passante mémoire, pas à la puissance de calcul brute.

Implication pratique : sur les workloads de génération longue (chatbots, rédaction), une bande passante mémoire supérieure (H200 vs H100, DDR5 vs DDR4 pour le CPU offloading) a plus d’impact qu’un nombre de TFLOPS supérieur.

Guide pratique : optimiser la mémoire

Votre modèle tient-il en VRAM (poids + KV-cache estimé + overhead) ?
├── Oui → Maximisez le batch size et la longueur de contexte
│   ├── Activez PagedAttention (vLLM, par défaut)
│   ├── Activez le prefix caching si préfixes communs
│   └── Augmentez gpu_memory_utilization à 0.90-0.95
└── Non →
    ├── Quantifiez les poids (INT4 avec AWQ ou GPTQ)
    │   └── Toujours pas assez ? → Quantifiez le KV-cache (FP8)
    ├── Utilisez le tensor parallelism sur plusieurs GPU
    │   └── 2-8 GPU avec NVLink dans un même nœud
    ├── Offloadez le KV-cache vers la RAM CPU (LMCache)
    └── En dernier recours → Offloadez des couches vers le CPU (num_gpu dans Ollama/llama.cpp)

Bonnes pratiques

Calculez AVANT de déployer. Estimez la VRAM totale (poids + KV-cache × batch_size + overhead) avant de choisir votre GPU. L’erreur la plus coûteuse est de découvrir en production que la VRAM est insuffisante pour le batch size et le contexte ciblés.

Le KV-cache est votre variable d’ajustement. Si la VRAM est serrée, réduisez max_model_len au minimum nécessaire. Chaque token de contexte inutilisé réserve de la mémoire qui pourrait servir à augmenter le batch size (et donc le throughput).

Quantification des poids + KV-cache = double dividende. Poids en INT4 (AWQ/GPTQ, -75 % VRAM pour les poids) + KV-cache en FP8 (-50 % VRAM pour le cache) libèrent une quantité massive de mémoire pour augmenter le batch size ou le contexte. C’est la combinaison la plus efficace sur H100.

Utilisez vLLM ou TensorRT-LLM en production. Le HuggingFace Transformers natif gaspille 60-80 % de la mémoire KV-cache par fragmentation. La migration vers vLLM (PagedAttention, prefix caching, quantification KV intégrée) apporte un gain de throughput de 2-24× pour un effort d’intégration minimal.

Monitorez en continu. Suivez l’utilisation VRAM (nvidia-smi, Prometheus + gpu_exporter), le batch size effectif, le TTFT, et le throughput. L’objectif : 85-95 % d’utilisation VRAM. En dessous de 80 %, vous sous-utilisez le GPU. Au-dessus de 95 %, vous risquez des OOM.


Questions fréquentes sur la gestion mémoire des LLM

Combien de VRAM faut-il pour un modèle 70B ?

En FP16 (pleine précision) : ~140 Go pour les poids seuls, ce qui nécessite au minimum 2× H100 80 Go en tensor parallelism. En INT4 (quantifié Q4_K_M) : ~35 Go pour les poids, ce qui tient sur un seul H100 80 Go avec de la marge pour le KV-cache et le batch. Ajoutez ~2-34 Go de KV-cache par requête selon la longueur de contexte (2 Go pour 8K tokens, 34 Go pour 128K tokens en BF16). Pour le fine-tuning complet en BF16, comptez 560-840 Go (8× A100 80 Go). Pour le fine-tuning QLoRA, ~100 Go suffisent (2× A100 ou 2× H100).

Pourquoi mon modèle fait un OOM alors que les poids tiennent dans la VRAM ?

Le KV-cache et les activations consomment de la mémoire supplémentaire au runtime. Les poids ne sont qu’une partie du budget mémoire. Le KV-cache croît avec la longueur du contexte et le nombre de requêtes concurrentes. Si vous chargez un modèle 70B INT4 (35 Go) sur un H100 80 Go, il reste ~43 Go. Avec un contexte de 32K et un batch de 8, le KV-cache peut consommer ~17 Go, les activations ~3 Go, l’overhead ~1,5 Go, totalisant ~56,5 Go. C’est dans le budget. Mais augmentez le batch ou le contexte, et l’OOM arrive. Solution : réduisez max_model_len, le batch size, ou activez la quantification du KV-cache (FP8).

Quelle est la différence entre quantifier les poids et quantifier le KV-cache ?

La quantification des poids (AWQ, GPTQ, bitsandbytes NF4) réduit la taille du modèle stocké en VRAM. C’est une réduction fixe, appliquée une fois au chargement. La quantification du KV-cache (FP8, NVFP4) réduit la taille du cache d’attention, qui croît dynamiquement avec le contexte et le batch. Les deux sont complémentaires : quantifier les poids libère de la VRAM fixe, quantifier le KV-cache libère de la VRAM variable. La combinaison (INT4 poids + FP8 KV) est le meilleur rapport qualité/compression pour l’inférence production.

Comment choisir entre tensor parallelism et pipeline parallelism ?

Le tensor parallelism (TP) découpe chaque couche entre les GPU. Il nécessite une communication à chaque couche, donc des GPU connectés par NVLink (haute bande passante, basse latence). Idéal pour 2-8 GPU dans un même nœud. Le pipeline parallelism (PP) assigne des couches complètes à chaque GPU. Il nécessite moins de communication (seulement entre les stages) mais introduit des « bulles » d’inactivité. Idéal pour les clusters multi-nœuds. Pour les modèles 70B sur un nœud DGX (8 GPU), TP=4 ou TP=8 est le standard. Pour les modèles 405B+ sur plusieurs nœuds, combinez TP + PP.

L’offloading vers le CPU est-il viable pour la production ?

Pour les poids : non, sauf en dernier recours. L’offloading de couches vers le CPU (Ollama, llama.cpp avec num_gpu) divise le throughput par 3-5× car la RAM CPU est 30-60× plus lente que la HBM GPU. Pour le KV-cache : oui, dans certains cas. L’offloading du KV-cache des sessions inactives vers la RAM CPU (via LMCache) ajoute 10-50 ms de latence au rechargement, ce qui est acceptable si l’alternative est de recalculer tout le prefill (plusieurs secondes). En production, l’offloading KV est un compromis viable pour les applications avec des sessions intermittentes. L’offloading des poids est réservé aux environnements de développement ou aux démos.

Polydesk.ai — Footer