Salut les amis !
Petit rappel pour ceux qui débarquent : sur ce blog, je bench des LLMs en local sur Olares One (un mini-PC avec une RTX 5090M 24 Go, c’est mon labo perso). Trois “stacks” reviennent souvent : Genesis = la stack vLLM custom de Sandermage qui me tient à 88 t/s, Lucebox = un fork de llama.cpp ultra-tuné par sandropuppo, et MTP = le speculative decoding intégré nativement à Qwen3.6 et Gemma 4. Si vous voulez le détail, le post sur le choix de la machine explique le contexte.
Bref, une semaine étrange sur l’Olares One. Côté upstream tout bouge en même temps : Gemma 4 sort ses drafters MTP (le 5), vLLM intègre le support natif (le 6), llama.cpp suit (le 7 via un fork tiers), et pendant ce temps mon Lucebox v1.9.0 régresse de 22 % sans qu’on comprenne pourquoi. Petit récap des trois benches qui ont occupé mes après-midi, plus le ménage des 8 apps Qwen3.6 27B sur l’Olares One Market.
1) Gemma 4 E4B + MTP via vLLM = 178 t/s sur Blackwell consumer mobile
C’est le scoop qui mérite son post séparé (lien à la fin), mais en deux mots : PR #41745 mergée le 6 mai à 14:39 UTC ajoute le support natif des drafters Multi-Token Prediction de Gemma 4 (E2B/E4B/26B-A4B/31B). Le 7 mai à 06:13 UTC, le nightly Docker est publié. À 06:35 UTC, mon Olares One sort :
Run 1 (cold): 800 tok in 6,17 s = 129,73 t/s
Run 2: 800 tok in 4,17 s = 191,73 t/s
Run 3: 800 tok in 3,73 s = 214,38 t/s
AVG = 178,6 t/s, 77,3 % acceptance. Pas de Genesis, pas de fork, pas de patch — juste vllm/vllm-openai:nightly-1acd67a795... + le bon --speculative-config. Je détaille tout ça dans le post court de cet aprèm.
Le truc à retenir ici : 24 heures entre merge upstream et bench validé sur Blackwell consumer mobile. Y a un an cette boucle aurait pris 2-3 semaines.
2) Lucebox v1.9.0 — la régression mystère 88 → 69 t/s
Là c’est plus tordu. Le 4 mai j’avais bench Lucebox v1.4.4 (chemin DFlash custom, target Qwen3.6-27B Q4_K_M, drafter z-lab BF16) à 88,5 t/s sur ce même Olares One. Quand j’ai voulu rebench le 7 mai après le rebuild v1.9.0 (qui inclut PR #94 matched 3.6 SWA draft + PR #99 consumer Blackwell fix), j’obtiens systématiquement 69 t/s — sur exactement le même hardware, le même chart, les mêmes drafters cachés sur disque.
J’ai isolé hypothèse par hypothèse :
- PR #99 (consumer Blackwell fix) ? Désactivable en passant à l’image v1.6.0 (sans le flag) → toujours 69 t/s. Pas le coupable.
fa_window=2048default ? OverrideDFLASH27B_FA_WINDOW=0(full attention, comme v1.4.4) → toujours 69 t/s.- TQ3_0 KV vs Q4_0 KV (la doc Lucebox laissait planer le doute) ? Bench à
--cache-type-k q4_0 --cache-type-v q4_0→ 68,96 t/s. Identique à TQ3_0. - GPU power throttle ?
nvidia-smipendant l’inférence montre 2 200 MHz / 70-83 % util — pas throttlé. - HAMi
0mparsing bug ? OverrideCUDA_DEVICE_MEMORY_LIMIT_0=24300m→ boot OK, daemon ready, toujours 69 t/s.
Tableau récap :
| Config | t/s |
|---|---|
| v1.9.0 (PR #94+#99, TQ3_0, fa_window=2048) | 68,85 |
| v1.9.0 + fa_window=0 | 68,83 |
| v1.6.0 (PR #94 only, TQ3_0) | 69,05 |
| v1.9.0 + Q4_0 KV | 68,96 |
| Référence v1.4.4 du 4 mai | 88,5 |
Toutes les hypothèses image/config sont éliminées. Reste l’environnement device : update kernel, HAMi runtime, libvgpu state, ou méthodologie de bench (le 4 mai j’ai peut-être bench via un path Open WebUI / public auth URL avec HTTP/2, alors que là je passe par kubectl exec localhost). Le kubectl exec route à travers la stack k8s qui peut ajouter des microsecondes, mais pas 22 % à mon avis.
Conclusion provisoire : régression environnementale qu’on ne peut pas isoler sans accès device-level. J’ai documenté ça dans la mémoire long-terme et désactivé Lucebox de l’app store (voir §4).
3) vLLM no-Genesis avec PR #39931 — la moitié-fix qui pète sur le workspace lock
Côté vLLM, JartX intègre dans le code source officiel le 5 mai à 00:14 UTC la PR #39931 : “[Feature] TurboQuant: support hybrid models and uniform quantization”. C’est gros : ça fixe le crash que TurboQuant lançait dès qu’il croisait une couche Mamba sur Qwen3.5/3.6/Qwen3-Next. Trois des 28 patches Genesis deviennent inutiles d’un coup (P60 ngram pour GDN, P65 baisse de mode CUDA graph en spec-decode, P66 filtre tailles invalides à la capture).
J’ai testé sur l’image vllm/vllm-openai:gemma4-0505-cu130 (build main HEAD post-merge) avec exactement la config de mon vllmqwen36turbo27bone, mais zéro var Genesis.
Premier crash, paradoxal : EagleProposer.__init__ → torch.zeros(...) → CUDA driver error: invalid argument. C’est le bug HAMi 0m que je connaissais déjà côté Lucebox/Driver API. Je pensais que vLLM y échappait (Runtime API). Faux : le drafter MTP allocation passe par un path qu’HAMi-core intercepte. Workaround CUDA_DEVICE_MEMORY_LIMIT_0=24000m → boot.
Deuxième crash, attendu : capture CUDA graph sur query_start_loc.tolist() dans turboquant_attn.py:583. C’est exactement ce que P65 monkey-patche. Workaround : --enforce-eager (skip TOUS les CUDA graphs).
Bench :
Run 1: 800 tok in 11,12 s = 71,97 t/s
Run 2: 800 tok in 10,84 s = 73,81 t/s
Run 3: 800 tok in 11,13 s = 71,87 t/s
AVG = 72,55 t/s — −17,5 % vs Genesis baseline (88 t/s). C’est le coût de l’--enforce-eager. Genesis P65 fait plus malin : il downgrade _cudagraph_support seulement quand speculative_config est actif, donc les batches de décode 1-token gardent leur CUDA graph.
J’ai aussi tenté Patch 65 seul (3 lignes inline-patchées sur main HEAD). Boot OK, capture CUDA graph propre, log explicite : “setting cudagraph_mode=PIECEWISE”. Mais à la première inférence : crash.
File ".../vllm/v1/attention/backends/turboquant_attn.py", line 862, in _decode_attention
current_workspace_manager().get_simultaneous(...)
AssertionError: Workspace is locked but allocation requires 0,76 MB,
current size is 0,00 MB. Workspace growth is not allowed after locking.
C’est le bug du torch.empty invisible au profiler dans la phase de “continuation prefill”, que Sandermage décrit dans le thread #40807 — ses patches P22 (partage du buffer de déquantization + pré-allocation du workspace K/V) et P38 (machine d’états mémoire) sont le vrai fix. P65 seul ne suffit pas. Pour vraiment quitter Genesis, il faut au minimum P22 + P38 + P65.
J’ai posté un comment sur #40807 avec ces deux datapoints (4ème hardware confirmé après Ampere 3090, A5000, et Blackwell 5080). Toujours pas de réponse au 8 mai à 11 h.
Conclusion : on ne peut pas drop Genesis aujourd’hui sans porter ~5 patches upstream non-triviaux. PR #39931 est nécessaire mais pas suffisant. La fix complète demande Sandermage ou un core-team motivé.
4) Le ménage : 8 apps Qwen3.6 27B sur l’Olares One Market → 2
À force d’itérer sur les stacks, mon Olares One Market avait 8 variantes de Qwen3.6 27B :
vllmqwen36turbo27bone(Genesis vLLM, 88 t/s)lucedflashqwen36one(Lucebox DFlash, 69 t/s today / 88 ref)dflashqwen36one(buun-llama-cpp, 80 t/s)llamacppqwen36mtpone(llama.cpp + PR #22673, 65 t/s @ 128K)llamacppqwen36dense27bone(llama.cpp + NVFP4 + ngram, non bench récemment)vllmqwen36dense27bone(vLLM 0.19.1 vanilla — crashloop)- Plus deux autres pour 35B-A3B.
Trop. J’ai gardé deux apps pour 27B, et pour aider les utilisateurs à choisir, les titres les positionnent directement :
| App | Title | Stack | Context |
|---|---|---|---|
| ⚡ | Qwen36 27B Fast | vLLM Turbo + Genesis 28 patches + TurboQuant K8V4 + MTP n=3 | 128 K |
| 📜 | Qwen36 27B Long Context | llama.cpp + PR #22673 (custom image) + froggeric MTP-GGUF | 128 K (262 K natif) |
Supprimés : lucedflashqwen36one, dflashqwen36one, vllmqwen36dense27bone, llamacppqwen36dense27bone. Les apps 35B-A3B (llamacppqwen36a3bone + qwen36a3bvisionone) restent — modèles différents.
Au passage j’ai aussi attrapé 2 bugs sur la version “Fast” pendant le re-test :
- v2.5.2 → v2.5.3 : drop
--prefix-caching-hash-algo xxhash. L’image custom Genesis n’a pas le packagexxhashinstallé → crash après 6 requêtes :ModuleNotFoundError: xxhash. - v2.5.4 → v2.5.5 : revert
GENESIS_ENABLE_P5B_KV=1. P5B (KV cache pad-smaller-to-max strategy, ~34 % per-block VRAM saving) plante en CUDA OOM à la première inférence sur 24 GB — combiné avec P38 prealloc, on dépasse les 24 300 m du HAMi cap. Mémoire disait que P5B était incompatible avecturboquant_4bit_nc; manifestement c’est aussi cassé avec K8V4 sur 24 GB.
Tout ça pour finalement boot v2.5.6 à 128 K context stable.
5) Le bonus : Atomic Chat fork pour Gemma 4 MTP via llama.cpp
Pour les gens qui veulent Gemma 4 MTP côté llama.cpp (pas vLLM), AtomicChat a publié les GGUFs MTP compatibles + un fork AtomicBot-ai/atomic-llama-cpp-turboquant qui ajoute :
- L’arch
gemma4_assistantMTP drafter (avec centroid LM head pour E2B/E4B) - TurboQuant KV-cache dans llama.cpp (
-ctk turbo3 -ctv turbo3) - Les flags runtime
--mtp-head/--spec-type mtp
J’ai built leur fork pour sm_120 → image aamsellem/llamacpp-atomic-mtp:0.1.0 (2,72 GB). Bench AtomicChat sur M5Max : 97 → 138 t/s sur Gemma 4 26B (+40 %).
Validé sur Olares One ce 8 mai après-midi :
Gemma 4 E2B + MTP : 206,56 t/s, 60,9 % acceptance (3 198 tok in 15,48 s)
Gemma 4 26B-A4B + MTP : 140,03 t/s, 78,1 % acceptance (3 238 tok in 23,12 s)
Le 26B-A4B livre 140 t/s avec 78 % acceptance sur premier run (hors warm-up). On dépasse le bench M5Max d’AtomicChat (138 t/s) — la 5090M sm_120 mobile a ~75 % de la bandwidth d’une 4090 mais le MoE Gemma 4 (3,8 B activés sur 26 B) y tire bien parti. Le E2B à 206 t/s c’est essentiellement la limite “single-stream max” pour un modèle 5 B sur ce hardware.
Charts en prod : gemma4e2bone v1.0.2 (E2B, 128 K ctx) et gemma426ba4bone v1.0.9 (26B-A4B, 64 K ctx, sans vision parce que mmproj n’est pas wired avec MTP dans ce fork).
Ce qui marche, ce qui pète — cheat sheet
| Stack | Statut sur Olares One 5090M |
|---|---|
| Atomic Chat fork + Gemma 4 E2B MTP via llama.cpp | ✅ 206 t/s, 61 % acceptance — single-stream cap |
| vLLM nightly + #41745 + Gemma 4 E4B MTP | ✅ 178 t/s, 77 % acceptance, stack 100 % upstream |
| Atomic Chat fork + Gemma 4 26B-A4B MTP via llama.cpp | ✅ 140 t/s, 78 % acceptance — bat le bench M5Max ref (138 t/s) |
| vLLM Turbo (Genesis) + Qwen3.6-27B + MTP n=3 + 88 K | ✅ 88 t/s steady, validé après fix HAMi + drop xxhash + revert P5B |
| llama.cpp + PR #22673 + Qwen3.6-27B-MTP + 128 K | ✅ 65 t/s, stack quasi-upstream (1 PR + 1 GGUF) |
| Lucebox DFlash v1.9.0 + Qwen3.6-27B | ⚠️ 69 t/s reproductible vs 88 t/s ref du 4 mai (régression non isolée) |
vLLM main HEAD + #39931 + --enforce-eager (no Genesis) | ⚠️ 72,55 t/s — pas viable pour drop Genesis |
| vLLM main HEAD + Patch 65 seul | ❌ Crash workspace lock — il faut aussi P22 + P38 |
La seule conclusion honnête
L’écosystème inférence local en 2026 a un rythme de release que personne ne peut suivre tous les jours. Sur 7 jours j’ai vu : un drafter Gemma 4 MTP officiel + 2 PR pour le supporter (vLLM + llama.cpp), une PR Genesis-compat dans vLLM main, deux régressions reproduisibles, et un fork TurboQuant porté à llama.cpp. Mon job sur Olares One Market c’est de trier : garder ce qui est validé, supprimer ce qui est cassé, écrire ce qui aide d’autres possesseurs Olares One à éviter mes crashes.
Côté Gemma 4 MTP via vLLM, c’est trop frais pour être généralisé sur autre chose qu’une 5090M. Si vous tournez sur 5070, 5080, 4090 / 3090 desktop, vos chiffres m’intéressent. Et si vous avez résolu la régression Lucebox 88 → 69 t/s d’une façon ou d’une autre, je veux savoir.
À 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.