Salut les amis !
Vous savez ces posts Reddit qui vous arrêtent net dans votre scroll du soir ? Celui-là, c’était la semaine dernière sur r/LocalLLaMA :
« 135 t/s on Qwen3.6 27B Q5 + vision + 200K context on a single RTX 3090. »
135 tokens par seconde. Sur RTX 3090. Avec 200K de contexte. Et la dernière partie qui pique : un fork appelé BeeLlama.cpp dont personne n’a entendu parler il y a quinze jours.
Mon meilleur path sur Olares One (RTX 5090M Laptop, 24 Go sm_120 Blackwell consumer mobile) plafonne à 88 t/s avec vLLM + Genesis (la stack de 28 monkey-patches de Sandermage qu’on traîne depuis avril) + MTP n=3, ou à 72,75 t/s à 262K de contexte FULL avec llama.cpp + MTP. Donc soit cette claim Reddit était cherry-pickée, soit je laissais 50 % de perf sur la table sans le savoir.
J’ai testé. C’est pas cherry-pické. 107 t/s à 262K full, +48 % sur mon path le plus rapide à long contexte. Voilà l’histoire.
Petit rappel pour ceux qui débarquent
Sur ce blog je publie depuis avril des benches d’inférence LLM en local sur Olares One (un mini-PC avec un RTX 5090M 24 Go, c’est mon labo perso). Trois noms reviennent :
- Genesis : la stack vLLM custom de Sandermage, 28 patches Python à maintenir, me tient à 88 t/s
- DFlash : la technique de speculative decoding par block diffusion drafter (papier ICLR 2026, code chez z-lab)
- MTP : Multi-Token Prediction, la tête de spec decoding intégrée nativement à Qwen3.6 et Gemma 4
BeeLlama est le dernier maillon d’une chaîne de quatre forks llama.cpp empilés les uns sur les autres : ggml-org/llama.cpp → TheTom/llama-cpp-turboquant (ajoute le KV cache TurboQuant 2/3/4-bit) → spiritbuun/buun-llama-cpp (ajoute DFlash) → Anbeeld/beellama.cpp (ajoute MTP + CopySpec + reasoning-loop protection). Aucun n’est mergé upstream, aucun ne publie d’image Linux pré-buildée, aucun ne cible Blackwell consumer sm_120. Je tournais déjà sur l’avant-dernier (buun-llama-cpp) via ma propre image — perf plafonnée à 76 t/s à 96K.
Quand la release BeeLlama v0.1.1 est tombée le 11 mai par-dessus, elle prétendait supporter MTP en plus du TurboQuant + DFlash hérités. Reddit annonçait 135 t/s.
Le matin : 50 minutes de build qemu
Olares One tourne sous CUDA 13 et le RTX 5090M est sm_120 Blackwell consumer mobile — pas sm_121 (DGX Spark) ni sm_100 (B100/B200 datacenter). Il me faut une image spécifique. Mon Mac est arm64, donc Docker buildx avec émulation qemu.
cd ~/dev/beellama.cpp # cloné -b v0.1.1
docker buildx build \
--platform linux/amd64 \
--build-arg CUDA_VERSION=13.0.0 \
--build-arg CUDA_DOCKER_ARCH=120 \
-f .devops/cuda.Dockerfile \
--target server \
-t aamsellem/beellama-cpp:0.1.1 .
50 minutes de cross-compile. L’instanciation des templates CUDA sous qemu est lente. Image finale : 2,67 Go. Push Docker Hub. Pendant que ça tournait, j’ai relu la doc BeeLlama et préparé mon chart Helm.
Midi : le premier crash
Pod déployé. Le target charge… et fail immédiat :
done_getting_tensors: wrong number of tensors; expected 866, got 862
Aïe. J’avais havenoammo/Qwen3.6-27B-MTP-UD-GGUF en cache sur le device (le GGUF MTP-baked que j’utilise pour l’app long-contexte MTP, raconté dans le post havenoammo). La tête MTP cuit 4 tenseurs supplémentaires dans le GGUF que le loader BeeLlama ne reconnaît pas. La recette veut la variante non-MTP d’unsloth — BeeLlama utilise DFlash spec decoding, pas MTP, pour Qwen3.6.
Swap target → unsloth/Qwen3.6-27B-UD-Q3_K_XL.gguf (14,5 Go). Drafter conservé : spiritbuun/Qwen3.6-27B-DFlash-GGUF Q8_0 (1,85 Go). Pod redéploie. Cette fois ça boot.
La config qui marche
Pour reproduire :
TARGET_URL: unsloth/Qwen3.6-27B-UD-Q3_K_XL.gguf # 14,5 Go
DRAFT_URL: spiritbuun/Qwen3.6-27B-DFlash-GGUF (q8_0) # 1,85 Go
# Args du serveur
--spec-type dflash
--spec-dflash-cross-ctx 1024
-ngl 99 --spec-draft-ngl 99
--ctx-size 262144
--cache-type-k turbo3 --cache-type-v turbo3 # 3-bit Walsh-Hadamard rotated KV
--batch-size 2048 --ubatch-size 256
--parallel 1 --kv-unified
--flash-attn on --jinja --no-mmap --mlock
--temp 0.6 --top-k 20 --min-p 0.0
L’instant
Premier bench à 96K (la valeur de la doc). Je lance dix runs Space Invaders consécutifs, max_tokens=800.
AVG 106,67 t/s [97,84 - 115,36]
Bingo, déjà dans la cour annoncée. Je pousse à 128K. Puis 200K. Puis 262K — le contexte natif maximum de Qwen3.6.
| Contexte | Runs | AVG t/s | Range | KV cache (turbo3) |
|---|---|---|---|---|
| 96K | 10 | 106,67 | 97,84 – 115,36 | ~3 Go |
| 128K | 5 | 116,0 | 107,12 – 127,32 | ~4 Go |
| 200K | 5 | 108,5 | 100,51 – 122,82 | ~6 Go |
| 262K (full natif) | 10 | 107,54 | 101,70 – 119,38 | ~8 Go |
La perf est essentiellement plate à travers les tailles de contexte. Le KV cache turbo3 (3-bit Walsh-Hadamard rotated) compresse assez agressivement pour que même au contexte 262K complet, toute la stack tienne sur 24 Go de VRAM avec marge. Zéro cycle de dégradation comme celui que j’avais documenté sur Gemma 4 DFlash, zéro CUDA OOM runtime. Le pic à 128K est une curiosité — sûrement les batches qui s’alignent mieux avec les tailles de capture cudagraph.
Le classement après cette nuit
| Stack | t/s avg | Contexte | Coût de maintenance |
|---|---|---|---|
| llama.cpp standard (sans spec) | 33-36 | 32K | upstream pur |
llamacppqwen36dflashone (buun-llama-cpp) | 76 | 96K | image custom |
llamacppqwen36mtpone (am17an MTP) | 72,75 | 262K | 1 PR open + image custom |
vllmqwen36turbo27bone (Genesis) | 88 | 88K | 28 patches + image 5 Go |
llamacppqwen36beellamaone (BeeLlama) | 107,54 | 262K | image custom one-time |
+48 % vs MTP @ 262K, +22 % vs vLLM Turbo @ 88K, +40 % vs buun DFlash @ 96K. Sur toutes les dimensions, c’est un upgrade strict — vitesse ET contexte ET stabilité.
Pourquoi c’est +48 % vs MTP au même contexte
Trois différences entre BeeLlama et mon llamacppqwen36mtpone v1.0.8 (qui tournait sur la branche am17an MTP) :
-
DFlash vs MTP pour Qwen3.6. Le DFlash drafter Q8_0 de spiritbuun a une acceptance nettement plus haute que la tête MTP baked dans le GGUF de havenoammo. Probablement parce que z-lab l’a tuné spécifiquement sur la distribution de sortie Qwen3.6, alors que la tête MTP est générique.
-
turbo3 KV vs q4_0 KV. turbo3 est 3-bit avec rotation Walsh-Hadamard, ~25 % plus petit que q4_0 (qui fait 4,5 bpv réels). Moins de pression mémoire = plus gros compute buffer = plus gros batch = meilleur throughput. La branche am17an MTP ne supporte pas turbo3, je suis bloqué sur q4_0.
-
batch 2048 / ubatch 256 vs 512/512. La recette BeeLlama utilise un batch top-level 4× plus grand avec un ubatch plus petit. Plus de prefill par cycle de scheduler. Le setup MTP est bottlenecked par le nombre de séquences dual-buffer DFlash et ne peut pas aller aussi large.
Ce que ça coûte vs ce que ça gagne
Coûts :
- ~50 min de build Docker custom one-time (ou pull
aamsellem/beellama-cpp:0.1.1si vous me faites confiance) - L’image est non-maintenue par rapport à upstream — BeeLlama n’a pas sync master depuis le 23 avril. Les nouveaux builds llama.cpp (b9130+) n’arriveront pas sur ce fork à moins qu’Anbeeld ne rebase
- Multi-GPU cassé dans ce fork (issue #7) — single-GPU only, ce qui est fine pour Olares One
- Détection d’arch DFlash fragile (issue #4) — confirmé par mon crash au load du GGUF MTP-baked
Gains :
- 107 t/s à 262K FULL sur Qwen3.6 27B sur un seul GPU Blackwell consumer mobile 24 Go
- Pas de poids de maintenance des 28 patches Genesis
- Pas de cycle de dégradation 5-fast/4-slow
- Pas de CUDA OOM runtime à long contexte
La leçon de la nuit
Cette claim Reddit que je trouvais ridicule il y a une semaine, elle était bonne. Et la raison pour laquelle j’étais à 88 t/s au lieu de 107, ce n’était pas une question de hardware (j’ai une 5090M, lui avait une 3090). C’était une question de savoir quel fork installer parmi quatre niveaux d’empilement non documentés sur consumer Blackwell.
BeeLlama vient de rendre obsolètes trois apps de mon catalogue (llamacppqwen36dflashone, llamacppqwen36mtpone, et en bonne partie vllmqwen36turbo27bone) en une nuit de build. C’est la troisième fois en six semaines qu’un fork de niche que personne ne suit débloque une perf que personne d’autre ne peut reproduire sur ce hardware (spiritbuun en mai, havenoammo le 8 mai, Anbeeld aujourd’hui).
Le réflexe que je continue d’installer : avant de patcher du code, scanner les forks tier-3 sur GitHub. UD, Dynamic, MTP-preserved, Heretic, BeeLlama. Il y a un écosystème de gens silencieux qui font des trucs extrêmement précis, et qui ne font pas de bruit sur Twitter ou Reddit.
Pour reproduire
App llamacppqwen36beellamaone v1.0.1 sur ma source de market Olares :
Olares Market → Settings → Add source
https://orales-one-market.aamsellem.workers.dev
Tout est dans orales-one-market — Helm chart, tag d’image exact, tous les flags, harnais de bench. L’app apparaît sous 5 minutes.
Si vous tournez une autre carte Blackwell consumer (5070 Ti, 5080, 5090 desktop, 5090M), l’image aamsellem/beellama-cpp:0.1.1 devrait marcher pour n’importe quel sm_120. Un 5090 desktop avec 32 Go et 1,79 To/s de bandwidth devrait atterrir vers 150-180 t/s si le scaling mobile→desktop pour cette stack tient. Si vous testez, partagez vos chiffres.
Prochaines étapes
- Surveiller la sync master de BeeLlama (pour récupérer le travail MTP d’am17an qui finira par merger)
- Tester CopySpec (l’ajout unique de BeeLlama par-dessus buun) sur des workloads de sortie structurée type replay de tool-call JSON — mon bench actuel est de la génération HTML open-ended où CopySpec a peu de matches
- Re-tester en q4_0 KV au lieu de turbo3 pour isoler si le gain vient de turbo3 ou des autres optims du moteur
Voilà ! Si vous reproduisez ces 107 t/s ou si vous trouvez un sweet spot encore plus haut sur un autre Blackwell consumer, envoyez-moi vos chiffres. À très vite !
Disclosure — Tous les benchmarks de ce post tournent sur mon propre Olares One. Si le contenu vous a été utile et que vous envisagez d’en acheter un, commander via ce lien de parrainage vous donne 400 $ de réduction (3 599 $ au lieu de 3 999 $) et me rapporte 200 $. Je le mentionne par transparence — et oui, accessoirement, ça aide à faire vivre le blog (hébergement, domaine, et le temps que je passe à écrire ici). Lien valable jusqu’à fin juin 2026 environ.