Vous avez déployé votre modèle de langage (LLM) sur un GPU puissant, mais les coûts explosent et la latence reste imprévisible. Le problème n'est pas votre modèle, c'est probablement la façon dont vous lui envoyez les données. Sans une gestion intelligente du flux de requêtes, vos cartes graphiques passent plus de temps à attendre qu'à calculer. C'est ici que deux techniques fondamentales entrent en jeu : le Continuous Batching et le KV Caching. Ensemble, ils transforment une infrastructure coûteuse et inefficace en une machine productive capable de traiter des centaines de requêtes simultanément.
Le problème du "Static Batching" : Pourquoi vos GPUs sont inactifs
Pour comprendre pourquoi le continuous batching est nécessaire, il faut d'abord regarder ce qui se passe dans l'approche traditionnelle, appelée "static batching". Imaginez que vous gérez une file d'attente pour un ascenseur. Dans le static batching, l'ascenseur ne part que lorsque tout le monde est dedans ET que tout le monde est arrivé à son étage. Si une personne descend au 2ème étage et une autre au 20ème, l'ascenseur attend que celle du 20ème arrive avant de pouvoir accepter de nouveaux passagers pour le prochain cycle.
Dans le contexte des LLMs, cela signifie que si vous groupez deux requêtes ensemble, le système attendra que la plus longue soit terminée avant de libérer la place pour de nouvelles requêtes. Concrètement, si une requête génère 50 tokens et une autre 2 000 tokens, le GPU reste partiellement inactif pendant la génération des 1 950 derniers tokens de la deuxième requête. Cette attente est purement gaspillée. Les processeurs graphiques modernes contiennent des milliers de cœurs conçus pour le parallélisme massif ; leur laisser faire des pauses entre les requêtes revient à payer un salaire complet pour un employé qui travaille à mi-temps.
KV Caching : Arrêter de recalculer l'historique
Avant même de parler de batch, il y a le problème du calcul redondant. Les modèles Transformer fonctionnent de manière auto-régressive : ils génèrent un token à la fois. Pour prédire le mot suivant, le modèle doit prendre en compte tous les mots précédents. Sans optimisation, chaque nouveau token nécessite de recalculer l'attention pour toute la séquence précédente. La complexité computationnelle devient alors quadratique, notée O(n²), ce qui ralentit exponentiellement la génération à mesure que la réponse s'allonge.
C'est là qu'intervient le KV Caching, une technique qui stocke les vecteurs clés (Key) et valeurs (Value) déjà calculés pour éviter de les recalculer à chaque étape. En sauvegardant ces représentations en mémoire VRAM, le coût de calcul pour chaque token suivant chute de O(n²) à O(n). Vous payez un prix en mémoire linéaire O(n), mais vous gagnez un temps de calcul considérable.
La taille de cette mémoire cache dépend directement de l'architecture du modèle. Pour un modèle avec L couches d'attention, H têtes d'attention et une dimension A par tête, la mémoire nécessaire pour stocker un seul token est de 2*L*A*H. Le facteur 2 vient du fait qu'on stocke à la fois les Clés (K) et les Valeurs (V). Pour des modèles grands comme Llama-3-70B ou Mixtral, cette consommation mémoire peut rapidement devenir le goulot d'étranglement principal, limitant le nombre de requêtes concurrentes possibles.
Continuous Batching : Le niveau token remplace le niveau requête
Le Continuous Batching, également connu sous le nom de "in-flight batching" ou "dynamic batching", résout le problème de l'attente décrite plus haut. Au lieu de gérer des lots de requêtes entières, le système gère des lots de tokens individuels à chaque étape de décodage.
Fonctionnellement, cela repose sur quatre principes opérationnels :
- Admission continue : Le planificateur accepte de nouvelles requêtes à chaque itération, sans attendre la fin d'un cycle de batch.
- Éviction immédiate : Dès qu'une requête génère son dernier token, elle est retirée du batch actif instantanément.
- Schéma dynamique : De nouvelles requêtes en attente prennent immédiatement la place libérée pour la prochaine étape de décodage.
- Streaming continu : Les clients reçoivent les tokens dès leur génération, améliorant la perception de la latence pour l'utilisateur final.
Cette approche maintient une utilisation élevée du GPU, quelle que soit la variance des longueurs de sortie. Que vous serviez des réponses courtes type chatbot ou des analyses longues, le processeur reste saturé de travail utile.
PagedAttention : Gérer la fragmentation de la mémoire
Même avec le continuous batching et le KV caching, un problème persiste : la fragmentation de la mémoire. Traditionnellement, on réservait un bloc contigu de mémoire pour chaque requête, basé sur la longueur maximale potentielle. Si un utilisateur ne tape que 100 mots mais que vous aviez réservé de la place pour 4 000, vous gaspillez de précieuse VRAM. C'est ce qu'on appelle la fragmentation interne.
PagedAttention, introduit par l'équipe derrière vLLM, applique le concept de pagination des systèmes d'exploitation classiques aux LLMs. Au lieu de blocs contigus, la mémoire GPU est divisée en petites pages de taille fixe. Le cache KV peut être stocké de manière non-contiguë. Une page terminée retourne dans le pool de mémoire disponible. Cela permet :
- D'éliminer presque entièrement la fragmentation interne.
- D'augmenter radicalement le nombre de requêtes concurrentes (batch size effectif).
- D'allouer la mémoire à la demande, plutôt que de la bloquer préventivement.
Cette technique fonctionne en synergie directe avec le continuous batching. Sans une gestion mémoire aussi flexible, le continuous batching serait limité par la capacité à trouver des espaces libres suffisants pour insérer de nouvelles requêtes dynamiquement.
| Critère | Static Batching | Continuous Batching |
|---|---|---|
| Niveau de granularité | Requête entière | Token individuel |
| Utilisation GPU | Basse (attente des longs) | Haute (>80% possible) |
| Gestion mémoire | Contiguë, réservée max | Non-contiguë (PagedAttention) |
| Latence moyenne | Variable et élevée | Réduite et stable |
| Gain de débit | Baseline (1x) | 10x à 23x selon benchmarks |
Performances réelles : Ce que disent les chiffres
Les gains ne sont pas théoriques. Des benchmarks indépendants montrent des différences abyssales. Le système d'inférence open-source vLLM a démontré des améliorations de débit de 10 à 20 fois par rapport au static batching standard. D'autres tests réalisés par Anyscale ont rapporté jusqu'à 23 fois plus de débit dans des scénarios de production chargés. Même dans des configurations moins optimisées, un gain de 2 à 3 fois est typique.
Ces chiffres traduisent une réalité économique simple : avec le continuous batching, vous pouvez servir le même volume de trafic avec 5 à 10 fois moins de GPU. Ou inversement, avec le même budget hardware, vous pouvez absorber des pics de charge beaucoup plus importants sans dégrader la qualité de service.
Optimisations avancées : BatchLLM et partage de préfixes
La recherche ne s'arrête pas là. Un papier récent (arXiv:2412.03594, avril 2024) présente BatchLLM, qui pousse l'optimisation plus loin. BatchLLM réorganise les requêtes pour prioriser celles ayant un ratio élevé entre décodage et préfilling. Il utilise également un arbre de préfixes global construit à l'avance pour partager explicitement les contextes KV communs.
Par exemple, si dix utilisateurs posent la même question introductive avant de diverger, le système calcule le contexte initial une seule fois et le référence pour les dix requêtes. Cela réduit la pression mémoire et augmente la taille des batches de décodage. Ces techniques sont particulièrement utiles dans les environnements où les prompts partagent souvent des structures communes, comme les assistants juridiques ou médicaux utilisant des templates standardisés.
Implémentation pratique : vLLM vs TensorRT-LLM
Si vous construisez votre propre infrastructure, vous n'avez pas besoin de réinventer la roue. Deux solutions open-source dominent le marché :
- vLLM : Très populaire pour sa simplicité d'intégration et son implémentation pionnière de PagedAttention. Idéal pour les équipes cherchant à déployer rapidement avec un bon équilibre performance/facilité d'utilisation.
- TensorRT-LLM : Développé par NVIDIA, il offre des optimisations très poussées au niveau matériel, notamment pour les architectures GPU spécifiques. Plus complexe à configurer, mais potentiellement plus performant dans des scénarios ultra-spécialisés.
Tous deux intègrent le continuous batching, le KV caching et le chunked prefill (découpage des prompts longs pour respecter les contraintes mémoire). Le choix dépend souvent de votre expertise interne et de votre stack existant. Si vous utilisez déjà l'écosystème NVIDIA CUDA intensivement, TensorRT-LLM peut offrir des avantages marginaux. Pour la plupart des cas d'usage web standard, vLLM reste le choix par défaut recommandé par la communauté.
Contraintes et pièges à éviter
Malgré ses avantages, le continuous batching n'est pas une baguette magique. La pression mémoire reste la limite absolue. Les prompts très longs combinés à des sorties étendues gonflent le cache KV. Si vous atteignez la saturation de la VRAM, le temps jusqu'au premier token (TTFT) augmente et les tokens par seconde (TPS) chutent drastiquement.
De plus, la complexité opérationnelle augmente. Gérer un serveur d'inférence avec continuous batching nécessite de surveiller finement l'utilisation mémoire, les taux d'éviction du cache et la queue d'attente. Une mauvaise configuration peut mener à des "OOM errors" (Out Of Memory) fréquentes. Il est crucial de définir des limites claires sur la longueur maximale des prompts et des réponses, et d'utiliser des mécanismes de rejet gracieux lorsque le système est saturé.
FAQ
Quelle est la différence principale entre Static Batching et Continuous Batching ?
Le Static Batching traite des lots de requêtes entières et attend que toutes soient terminées avant de libérer la ressource, causant de l'inactivité GPU si les longueurs varient. Le Continuous Batching opère au niveau du token : il retire une requête dès qu'elle finit et insère immédiatement une nouvelle requête en attente, maintenant une utilisation GPU constante et élevée.
Pourquoi le KV Caching est-il essentiel pour les LLMs ?
Sans KV Caching, chaque nouveau token généré nécessiterait de recalculer l'attention pour tous les tokens précédents, rendant la génération extrêmement lente (complexité O(n²)). Le KV Caching stocke ces calculs en mémoire, réduisant la complexité à O(n) pour chaque token suivant, au prix d'une consommation mémoire linéaire.
Qu'est-ce que PagedAttention et comment aide-t-il ?
PagedAttention divise la mémoire GPU en petites pages fixes, permettant un stockage non-contigu du cache KV. Cela élimine la fragmentation interne causée par la réservation de blocs mémoires contigus trop grands, augmentant ainsi le nombre de requêtes pouvant être traitées simultanément.
Quels outils open-source implémentent le Continuous Batching ?
Les deux principales solutions open-source sont vLLM et TensorRT-LLM. vLLM est particulièrement reconnu pour son implémentation de PagedAttention et sa facilité d'intégration, tandis que TensorRT-LLM offre des optimisations profondes liées au matériel NVIDIA.
Quel est l'impact réel sur le débit (throughput) ?
Les benchmarks indiquent des gains de débit allant de 2x à 23x par rapport au static batching, selon la configuration et la charge. vLLM a notamment montré des améliorations de 10 à 20 fois dans des conditions de production typiques.