Contexte
Ma machine principale tourne actuellement en continu pour alimenter gguf-bench.com, un projet de benchmarks de modèles GGUF. Les résultats actuels sont peu exploitables — les mesures sont saturées, ce qui les rend peu significatives, donc je fais de nouveaux benchmarks. La machine va tourner encore plusieurs semaines à produire des données propres.
Résultat : impossible de l’utiliser pour de l’agentique au quotidien. D’où l’idée de ressortir du placard un vieux PC de gamer.
Le matériel : un ASUS G20AJ ressuscité
Le G20AJ, c’est un petit boîtier compact ASUS sorti il y a une dizaine d’années, typiquement vendu comme PC gaming de salon. Le mien a eu droit à une upgrade GPU à l’époque : une GTX 1070 8 GiB. La carte est ancienne, mais les 8 Go de VRAM restent une surface de jeu correcte pour de l’inférence quantisée. Et à l’époque on avait aussi upgrade la RAM, pour la passer à 16 GiB. C’était une machine sympa à l’époque, même si le form factor n’était pas des plus pratiques, c’est le moins qu’on puisse dire.
Pour résumer:
Hardware:
- CPU: Core i7 4790
- RAM: 16 GiB
- GPU: GTX-1070 8GiB VRAM
Software:
- OS: Ubuntu 22.04 LTS (c’est un peu vieux, mais elle fonctionnait déjà)
- version:
masterdu 26/05/2026 - CUDA: 12.8
- Driver: 580.159.04
J’avais déjà joué avec Qwen3.5 9B dessus — ça tourne sans problème. Mais j’avais envie de pousser le vice et d’essayer le modèle 35B A3B (architecture Mixture of Experts, 3B de paramètres actifs par token) donc forcément dans une quantification forte.
Le modèle : Qwen3-35B-A3B-UD-IQ2_M d’Unsloth
J’ai utilisé la version quantisée distribuée par Unsloth :
Qwen3-35B-A3B-UD-IQ2_M
La quantisation IQ2_M est agressive — on est sur du 2 bits environ pour la majorité des poids. En bossant sur gguf-bench, je me suis rendu compte que les modèles MoE que j’ai testés (Gemma4 et Qwen 3.6 35B) gardaient de très bonnes performances même en quantisation forte. Alors pourquoi ne pas essayer. Je ne vous cache pas que réussir à faire entrer un modèle de 35 Milliards de paramètre sur cette machine, c’est un peu sportif. En lisant des articles sur Reddit r/LocalLLama, certains utilisaient ce modèle avec 8 GiB DE VRAM. Il n’y a pas de raison de ne pas y arriver !
Il a fallu faire de nombreux essais, pour éviter des Out Of Memory dans la VRAM de la machine, mais je pense avoir trouvé un compromis correct.
Configuration utilisée
- Contexte : 65 536 tokens (64K)
- MTP activé (Multi-Token Prediction) : améliore légèrement le débit en génération
- Backend : llama.cpp
Performances observées
| Métrique | Valeur |
|---|---|
| Prefill (pp) | ~375 tokens/s |
| Génération (tg) | ~25 tokens/s |
Le prefill est honnête. Les 25 t/s en génération, c’est acceptable pour un usage quotidien et ça reste fluide à l’utilisation.
La mise en œuvre
La démarche habituelle pour compiler llama.cpp:
git clone https://github.com/ggml-org/llama.cpp.git
cd llama.cpp
Comme il s’agit d’une vieille architecture qui n’est plus activée par défaut, il est nécessaire de la préciser à la configuration de la compilation:
cmake -B build -DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES="61"
puis les classiques:
cmake --build build --config Release -j $(nproc) -t llama-server llama-cli
J’utilise le chat_template de froggeric qui a passé des heures à l’adapter pour que l’architecture Qwen 3.5/3.6 fonctionne bien avec llama.cpp (froggeric/Qwen-Fixed-Chat-Templates).
Et enfin l’appel, une fois que j’ai trouvé mes réglages:
./build/bin/llama-server -m /data/models/unsloth/Qwen3.6-35B-A3B-UD-IQ2_M.gguf \
--jinja \
--chat-template-file /data/models/templates/chat_template.jinja\
--parallel 1 \
--temp 0.7 \
--top-k 20 \
--top-p 0.95 \
--min-p 0 \
--no-context-shift \
-c 65536 \
--cache-type-k q4_0 \
--cache-type-v q4_0 \
--host 0.0.0.0 \
--port 8002 \
-fa on \
--spec-type draft-mtp \
--spec-draft-n-max 2 \
--spec-draft-p-min 0.0 \
-ub 512 \
--metrics \
--slots \
--log-file /tmp/llama.log \
-lv 4 \
-t 8 \
--threads-batch 8 \
--no-mmap \
--cache-ram 0
Pour comprendre les options:
- -m /data/models/unsloth/Qwen3.6-35B-A3B-UD-IQ2_M.gguf: le modèle à charger
- –jinja : On active l’utilisation du moteur de template de format de conversation jinja2
- –chat-template-file /data/models/templates/chat_template.jinja: On donne le template à utiliser
- –parallel 1: On ne sert qu’un client à la fois (pas de parallélisme)
- –temp 0.7, –top-k 20,–top-p 0.95 ,–min-p 0: recommandés par l’éditeur du modèle
- –no-context-shift: On désactive le context-shifting non compatible avec le MTP
- -c 65536: Taille du contexte (tokens du prompt et de la génération)
- –cache-type-k q4_0: KV Cache partie clé quantisé en q4_0
- –cache-type-v q4_0: KV Cache partie valeur quantisé en q4_0
- –host 0.0.0.0: On écoute sur le réseau (pas que en localhost)
- –port 8002: Sur le port 8002
- -fa on: Le flash-attention est activé. Nécessaire pour la quantisation du kv-cache
- –spec-type draft-mtp: On active le MTP (permet de générer des tokens supplémentaires plus rapidement)
- –spec-draft-n-max 2: nombre max de tokens spéculés par étape
- –spec-draft-p-min 0.0: On désactive le plancher de probabilité des token générés testés
- -ub 512: Nombre de token traités en parallèle en un seul appel au GPU en prefill (analyse du prompt)
- –metrics: On active les métriques de performance (pour la supervision)
- –slots: facultatif, mais pour la supervision
- –log-file /tmp/llama.log: On enregistre les logs de llama.cpp
- -lv 4: Avec un niveau 4 de log. Le niveau 3 est suffisant en prod
- -t 8: On utilise 8 threads pour la génération CPU
- –threads-batch 8: On utilise 8 threads pour le prefill (l’analyse du prompt)
- –no-mmap: On charge le modèle en VRAM et RAM au démarrage de llama.cpp et pas progressivement
- –cache-ram 0: On ne garde pas de cache des tenseurs qu’on a mis sur le GPU
Un petit benchmark de performance ?
J’ai essayé de trouver une bonne balance entre l’analyse de prompt et la génération de tokens. L’outil llama-benchy a été d’une grande aide. On voit que la génération reste stable autour de 23-25 tokens/seconde avec ces petits prompts, mais le prefill est autour de 375-400 tokens/seconde. Ce dernier est important en agentique.
À l’usage, et ce n’est pas dans la mesure de llama-benchy, on notera que quand le contexte est plein, la vitesse de prefill tombe autour de 180-200 t/s et la génération autour de 15 t/s.
yves@caspar:/data/llamacpp$ uvx llama-benchy --base-url http://localhost:8002/v1 --model Qwen/Qwen3.6-35B-A3B --pp 512 2048 --tg 128 --depth 0 4096 --runs 1
[...]
| model | test | t/s | peak t/s | ttfr (ms) | est_ppt (ms) | e2e_ttft (ms) |
|:---------------------|---------------:|--------------:|-------------:|----------------:|----------------:|----------------:|
| Qwen/Qwen3.6-35B-A3B | pp512 | 366.93 ± 0.00 | | 1398.88 ± 0.00 | 1398.07 ± 0.00 | 1398.88 ± 0.00 |
| Qwen/Qwen3.6-35B-A3B | tg128 | 25.78 ± 0.00 | 29.00 ± 0.00 | | | |
| Qwen/Qwen3.6-35B-A3B | pp2048 | 398.26 ± 0.00 | | 5145.69 ± 0.00 | 5144.88 ± 0.00 | 5145.69 ± 0.00 |
| Qwen/Qwen3.6-35B-A3B | tg128 | 25.67 ± 0.00 | 30.00 ± 0.00 | | | |
| Qwen/Qwen3.6-35B-A3B | pp512 @ d4096 | 379.80 ± 0.00 | | 12136.05 ± 0.00 | 12135.23 ± 0.00 | 12136.05 ± 0.00 |
| Qwen/Qwen3.6-35B-A3B | tg128 @ d4096 | 23.40 ± 0.00 | 29.00 ± 0.00 | | | |
| Qwen/Qwen3.6-35B-A3B | pp2048 @ d4096 | 375.03 ± 0.00 | | 16386.19 ± 0.00 | 16385.38 ± 0.00 | 16386.19 ± 0.00 |
| Qwen/Qwen3.6-35B-A3B | tg128 @ d4096 | 23.15 ± 0.00 | 28.00 ± 0.00 | | | |
llama-benchy (0.3.7)
date: 2026-05-26 20:01:14 | latency mode: api
Est-ce que ça vaut le coup ?
Pour piloter mes agents au quotidien, oui, clairement. Le 35B A3B prend de meilleures décisions que le 9B, comprend mieux les instructions complexes, et gère nettement mieux le raisonnement multi-étapes. Pour ce type d’usage, le gain est réel.
En revanche, pour de la programmation assistée, la quantification fait perdre trop de précision au modèle pour que ça soit fiable. Ça fonctionne, mais il faudra sans doute faire beaucoup plus d’aller retours pour un résultat fonctionnel, pour des choses simples. Les modèles plus grands en Q4 ou Q5 restent supérieurs sur les tâches de génération de code — la quantisation IQ2 commence à vraiment se faire sentir. Pour faire du développement je serais plus parti sur le modèle Qwen-3.6-27B, plus précis pour ces tâches là, mais il est beaucoup plus demandant en calculs.
Conclusion
Faire tourner un MoE 35B sur une GTX 1070 de 2016, c’est possible. Pas confortable, mais fonctionnel. Si vous avez du vieux matériel qui prend la poussière avec 8 Go de VRAM, ça vaut la peine d’explorer les quantisations ultra-compressées d’Unsloth — vous serez surpris de ce qu’on peut en tirer.