Tout est parti d'une vidéo YouTube.
Je regardais une présentation sur l'optimisation du KV cache avec Flash Attention — le genre de vidéo qui passe un dimanche soir quand on n'arrive pas à décrocher. Le présentateur expliquait comment la quantification du cache clé-valeur pouvait libérer de la VRAM sans impact perceptible sur la qualité. En théorie, c'était propre. Mais est-ce que ça marchait vraiment sur ma config ?
Du coup, j'ai passé la journée à tester. Gemma4, Qwen3.5, ma RTX 3050 6 Go, Ollama 0.23.1. Résultat : x3 de contexte sur gemma4 (10k → 32k), x10 sur qwen3.5 (8k → 80k), avec un gain de vitesse de 5 à 10 %. Le tout sans changer de matériel.
Avant de continuer de lire la suite de l'article, je vous invite à vous inscrire à ma newsletter, pour connaître en avant première les futurs sujets traités chaque semaine.
Le problème : pourquoi votre contexte est bridé sans que vous le sachiez
Quand vous lancez un modèle sur Ollama, deux choses consomment votre VRAM :
- Les poids du modèle (weights) — fixes, dépendent du nombre de paramètres et de la quantification
- Le KV cache — dynamique, il grandit avec chaque token de contexte
Sur une RTX 3050 6 Go, gemma4:e4b en Q4_K_M pèse déjà ~4,7 GiB rien qu'en poids. Le KV cache en précision FP16 standard gruge le reste. Résultat : à 10k tokens de contexte, vous êtes déjà au taquet. Au-delà, Ollama décharge des couches sur le CPU — et les performances s'effondrent.
Le pire, c'est que la plupart des gens ne savent même pas que c'est réglable. Ollama utilise FP16 pour le KV cache par défaut. Et FP16, c'est 2 octets par élément, pour chaque tête d'attention, pour chaque couche. Sur un modèle à 4 milliards de paramètres avec 32 têtes et 16 couches, ça chiffre vite.
La solution tient en deux variables d'environnement
Deux flags dans la configuration systemd d'Ollama changent tout :
| Variable | Valeur | Ce qu'elle fait |
|---|---|---|
OLLAMA_FLASH_ATTENTION |
1 |
Active le tiling de l'attention — réduit les écritures en mémoire GPU (HBM) et optimise la bande passante |
OLLAMA_KV_CACHE_TYPE |
q8_0 |
Quantifie le cache clé-valeur en 8 bits au lieu de 16 bits — divise par deux la consommation VRAM du KV cache |
Flash Attention, c'est l'algorithme de Tri Dao (Stanford, 2022) qui a révolutionné l'attention des transformers. Au lieu de calculer la matrice d'attention complète N×N — qui explose en O(N²) — il la découpe en tuiles et fusionne les opérations pour éviter d'écrire les résultats intermédiaires dans la mémoire principale. Moins d'écritures = moins de bande passante consommée = plus de place pour le contexte.
La quantification q8_0, elle, s'attaque directement au stockage : chaque élément du cache passe de 16 bits à 8 bits. La précision baisse, mais les benchmarks montrent que l'impact sur la qualité est négligeable pour les modèles de 4B+ paramètres.
Le combo des deux est redoutable. Flash Attention évite le gaspillage mémoire pendant le calcul, q8_0 réduit le stockage permanent.
Ma configuration de test
Avant de vous donner les chiffres, voici le matériel et la config :
- GPU : NVIDIA RTX 3050 6 Go Laptop (WSL2 Ubuntu, compute capability 8.6 — Ampere)
- Ollama : 0.23.1
- Modèles testés :
gemma4:e4b— Q4_K_M, 43 couches, text-only pruned (Handyfff)qwen3.5:4b— Q4_K_M, contexte 8k
La configuration appliquée dans /etc/systemd/system/ollama.service.d/port.conf :
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"
Environment="OLLAMA_CONTEXT_LENGTH=32768"
Pour gemma4, j'ai aussi créé un Modelfile avec PARAMETER num_ctx 32768 pour forcer le contexte maximum GPU-only. Sans ça, Ollama utilise son contexte par défaut (2048 tokens).
Résultat 1 — gemma4:e4b : de 10k à 32k sans changer de GPU
Voici les relevés VRAM pour chaque palier de contexte, avec la config FA + q8_0 :
| Contexte | VRAM | GPU Layers | Status |
|---|---|---|---|
| 4 096 | ~5 233 MiB | 43/43 | 100 % GPU |
| 8 192 | ~5 405 MiB | 43/43 | 100 % GPU |
| 16 384 | ~5 529 MiB | 43/43 | 100 % GPU |
| 24 576 | ~5 511 MiB | 43/43 | 100 % GPU |
| 32 768 | ~5 623 MiB | 43/43 | 100 % GPU — MAX |
| 36 864+ | ~3 200-3 700 | 42/43 | Output layer → CPU |
Le seuil critique est à 32 768 tokens. Au-delà, la couche de sortie bascule sur CPU et les performances chutent de 10-15 %. Mais 32k, c'est déjà trois fois plus que ce que j'avais avant (10k en FP16). Et tout tient dans les 6 Go de la RTX 3050.
Le coût du KV cache q8_0 est remarquablement linéaire : environ 36 MiB par tranche de 4k tokens supplémentaires. À 32k, le cache complet ne consomme que ~400 MiB de plus qu'à 4k.
Résultat 2 — qwen3.5:4b : le vrai gagnant, x10 de contexte
C'est sur qwen3.5:4b que la différence est la plus spectaculaire.
Sans optimisation, mon qwen3.5:4b calait à environ 8 192 tokens avant de déborder. Avec FA + q8_0, je l'ai poussé jusqu'à 80 000 tokens sans offload CPU. C'est un facteur 10.
| Modèle | Avant (FP16) | Après (FA + q8_0) | Gain |
|---|---|---|---|
| gemma4:e4b | ~10 240 tokens | 32 768 tokens | x3 |
| qwen3.5:4b | ~8 192 tokens | 80 000 tokens | x10 |
Pourquoi une telle différence entre les deux modèles ? qwen3.5:4b est plus léger en poids (~2,5 GiB en Q4_K_M contre ~4,7 GiB pour gemma4) — il reste donc beaucoup plus de marge pour le KV cache. La quantification libère une bande passante que le modèle peut entièrement convertir en contexte supplémentaire.
Résultat 3 — la vitesse ne baisse pas, elle augmente
C'est le résultat le plus contre-intuitif : non seulement le contexte s'élargit, mais la vitesse de génération augmente de 5 à 10 %.
gemma4:e4b — 4K vs 32K
| Contexte | TPS moyen | VRAM | Différence |
|---|---|---|---|
| 4 096 | 44,9 t/s | 5 248 MiB | — |
| 32 768 | 44,2 t/s | 5 640 MiB | -0,7 t/s (-1,5 %) |
La différence est imperceptible : -1,5 % de TPS pour 8× plus de contexte. Et comparé à mon benchmark d'avril 2026 où gemma4 tournait à 38 t/s en config standard, c'est +16 % de TPS avec Flash Attention activé.
Test de stabilité — 5 minutes continues
Pour vérifier que le gain n'est pas un artefact de bench court, j'ai fait tourner gemma4 en continu pendant 5 minutes :
| Métrique | Valeur |
|---|---|
| TPS moyen | 40,8 t/s |
| TPS stable (6/7 runs) | 42-45 t/s |
| Température GPU | 63-75 °C |
| VRAM stable | ~5 640 MiB |
Aucun throttling thermique. La 3050 tient la charge sans broncher.
Pour qwen3.5:4b, le TPS est passé d'environ 55 t/s à 60-62 t/s — un gain de 8-10 %. Le modèle est plus à l'aise avec le cache quantifié, probablement parce que la réduction de la pression mémoire permet au GPU de mieux paralléliser.
Pourquoi ça marche : explication technique
Le gain n'est pas magique — il découle de trois mécanismes distincts :
1. La quantification du KV cache réduit la pression mémoire. En passant de FP16 à q8_0, chaque élément du cache occupe 1 octet au lieu de 2. À 32k tokens, sur gemma4, ça représente environ 400 MiB d'économisés — exactement la marge qui permet de ne pas déborder des 6 Go.
2. Flash Attention fusionne les opérations pour éviter les écritures intermédiaires. L'attention standard écrit la matrice N×N complète en HBM avant de la normaliser. Flash Attention fait le calcul par tuiles en SRAM (on-chip), sans jamais écrire la matrice complète en mémoire externe. Résultat : moins de bande passante consommée, plus de place pour le contexte.
3. Sur GPU Ampere (compute 8.6), la SRAM est bien dimensionnée pour cette technique. La RTX 3050 a 2 Mo de L2 cache et 64 Ko de SRAM par SM — assez pour que le tiling de Flash Attention soit efficace sans overflow. Ollama 0.23.1 gère correctement le support Ampere, ce qui n'était pas le cas des versions antérieures.
Le résultat concret : les poids du modèle restent la consommation dominante (~4,5 GiB sur 5,2-5,6 GiB), le KV cache quantifié ajoute un coût marginal linéaire (~36 MiB/4k tokens), et la génération reste compute-bound plutôt que memory-bandwidth-bound.
Comment appliquer ça chez vous
La mise en place prend cinq minutes :
# 1. Créer un modèle avec le contexte souhaité (optionnel mais recommandé)
cat > /tmp/gemma4-32k.Modelfile << 'EOF'
FROM hf.co/Handyfff/Gemma-4-E4B-it-uncensored-pruned-TextOnly-EnglishOnly-GGUF:Q4_K_M
PARAMETER num_ctx 32768
EOF
ollama create gemma4:e4b-32k -f /tmp/gemma4-32k.Modelfile
# 2. Éditer la configuration systemd d'Ollama
sudo mkdir -p /etc/systemd/system/ollama.service.d
sudo tee /etc/systemd/system/ollama.service.d/port.conf << 'EOF'
Environment="OLLAMA_FLASH_ATTENTION=1"
Environment="OLLAMA_KV_CACHE_TYPE=q8_0"
Environment="OLLAMA_CONTEXT_LENGTH=32768"
EOF
# 3. Redémarrer le service
sudo systemctl daemon-reload
sudo systemctl restart ollama
# 4. Vérifier que c'est bien activé
journalctl -u ollama -n 5
Vous devez voir dans les logs une ligne du type :
FlashAttention:Enabled KvSize:32768 KvCacheType:q8_0
GPULayers:43[ID:GPU-xxx Layers:43(0..42)]
offloaded 43/43 layers to GPU
Si vous ne voyez pas FlashAttention:Enabled ou KvCacheType:q8_0, vérifiez que les variables sont bien dans le fichier de configuration systemd (pas dans un .env à côté — Ollama en service systemd ne lit pas les fichiers .env sauf si le service est configuré pour).
Ce qu'il faut retenir
Après une journée de benchmarks, trois leçons :
1. Le KV cache q8_0 est le facteur décisif, pas Flash Attention. La quantification du cache libère bien plus de VRAM que l'optimisation de l'algorithme d'attention. Flash Attention aide, mais c'est q8_0 qui permet de passer de 10k à 32k (ou 80k).
2. Pas besoin d'un modèle « court » et d'un modèle « long » séparés. La pénalité de vitesse entre 4k et 32k est de 1,5 % — négligeable en pratique. Un seul modèle configuré au max de ce que votre GPU peut tenir, et vous avez le meilleur des deux mondes.
3. 32k est le sweet spot GPU-only sur 6 Go. Au-delà, la couche de sortie tombe sur CPU et les performances chutent. Si vous avez 8 Go ou plus, vous pouvez viser plus haut — le principe reste le même : FA + q8_0, et pousser le contexte jusqu'au seuil de basculement CPU.
Est-ce que vous avez testé Flash Attention et le KV cache q8_0 sur votre config ? Quel gain de contexte avez-vous obtenu ? Dites-moi en commentaire — et si vous avez des résultats sur d'autres modèles (llama, mistral, deepseek), je suis preneur.
Pour aller plus loin
Sources et références externes :
- FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness — papier original de Tri Dao et al. (Stanford, 2022)
- Ollama GPU documentation — configuration des variables d'environnement GPU
- Ollama releases — support Flash Attention ajouté dans les versions récentes
Articles liés sur ce blog :
- Gemma 4 e2b et e4b : ce que j'ai appris après des heures de benchmark sur des modèles locaux
- J'ai benchmarké Qwen3 sur GPQA et HLE — les résultats m'ont surpris
- Benchmark de 30 modèles locaux — le classement définitif (avril 2026)
Qui suis je ?
Je suis Mathieu GRENIER, CTO d'Easystrat une startup de Montpellier, en France. Je manage une équipe d'une dizaine d'ingénieurs (Graphistes, IA, frontend, backend, devOps, AWS) en remote depuis le Japon.
J'ai aussi mon activité de freelance, où je conseille des entrepreneurs dans leurs projets d'application.
Avec mon expérience personnelle de plus de 15 ans en ESN, j'ai pu travailler pour un large panel d'entreprises de différentes tailles. Ma compréhension des problèmes métiers est une de mes grandes forces et permet à mes clients de pouvoir se projeter plus facilement.
L'essentiel de mon travail consiste à canaliser l'énergie des entrepreneurs sur l'essence même de leur projet.
La technologie, les méthodes, le management sont le cœur de mes compétences.
Vous pouvez me faire confiance sur ces points là.
Si vous voulez me parler d'un de vos projets, n'hésitez pas à m'envoyer un email avec vos disponibilités à : contact@mathieugrenier.fr
Je documente en public ce que je construis en privé. Abonnez-vous pour ne pas manquer les prochains articles sur l'IA locale, les agents autonomes et l'architecture de systèmes LLM.
Tous les articles de ce blog sont écrits par moi, même si je peux m'aider de l'IA pour illustrer mes propos. Mais jamais je ne fournis d'articles 100 % IA.
Comment j'ai multiplié par 10 le contexte de mes modèles locaux avec Flash Attention et KV Cache q8_0