Salut les amis !
Hier soir, Lucebox passait à 88,5 t/s sur mon Olares One et devenait le nouveau champion sur ce hardware, 0,5 t/s devant mon vLLM Turbo. Pas un blowout mais une victoire honnête. J’ai éteint le laptop content. Et puis ce matin, en buvant mon café, je vois passer ça sur le repo Lucebox-hub :
PR #94 — Support Qwen3.6-27B-DFlash draft (SWA layers) — 106 t/s on RTX 4090
106 tokens par seconde. Sur Ada Lovelace (RTX 4090). Avec un sweep DDTree budget qui montre +57 % vs le drafter 3.5 mismatched (67,4 → 106,1 t/s).
Si ça scale linéairement à mon RTX 5090M Laptop Blackwell consumer (qui a globalement plus de bandwidth), on devrait taper 120-130 t/s sur Olares. La saga continue, et je m’attends à un beau gain ce soir.
Spoiler dès maintenant : 88,7 t/s. +0,2 vs hier. Dans le bruit de mesure. Voilà l’enquête.
Petit rappel pour ceux qui débarquent
Lucebox c’est le fork llama.cpp de sandropuppo qui fait tourner DFlash speculative decoding avec des kernels CUDA custom. Le “drafter mismatched 3.5” qu’on utilise depuis le début, c’est le petit modèle qui propose les tokens à l’avance — sauf qu’il a été entraîné sur Qwen3.5, pas Qwen3.6 qu’on fait tourner. Ça marche, mais avec une acceptance moyenne. La PR #94 publie enfin un drafter spécifiquement entraîné pour Qwen3.6, avec en bonus un mécanisme d’attention SWA (Sliding Window Attention) qui devrait booster encore.
Sur le papier, c’est exactement la mise à jour qui me manquait.
Ce que la PR promet
Trois changements :
-
Matched 3.6 draft au lieu de mismatched 3.5 — quatre couches SWA + une couche full-attention au lieu de cinq couches full-attention. Layer types lus depuis
config.jsonà côté des safetensors. -
SWA mask plumbed dans
qwen3_dflash_graph.cppviaggml_flash_attn_ext. -
Build flag
DFLASH27B_ENABLE_BSA=ONactive Block-Sparse-Attention (path PFlash, mais aussi utile pour le drafter).
Le sweep RTX 4090 du PR :
budget=6: 93,9 t/s
budget=10: 106,1 t/s ← sweet spot
budget=14: 96,5
budget=18: 102,7
budget=22: 103,5
Sweet spot à 10. Logique : budget plus bas = moins de verify parallèle = moins de chunks = bien sur cartes avec bandwidth limité.
Le matin : le build
Je rebuild une image v1.6.0 sur cette branche :
RUN git clone --depth 1 --branch feat/dflash-qwen36-swa-draft \
--recurse-submodules \
https://github.com/Quitetall/lucebox-hub /src/lucebox
RUN cmake -B build -S . \
-DCMAKE_BUILD_TYPE=Release \
-DGGML_CUDA=ON -DGGML_CUDA_NO_VMM=ON \
-DCMAKE_CUDA_ARCHITECTURES="120" \
-DDFLASH27B_ENABLE_BSA=ON \
-DCMAKE_EXE_LINKER_FLAGS="-Wl,-rpath-link,/usr/local/cuda/lib64/stubs" \
-DCMAKE_SHARED_LINKER_FLAGS="-Wl,-rpath-link,/usr/local/cuda/lib64/stubs" \
&& cmake --build build --target test_dflash test_flashprefill_kernels -j$(nproc)
100 minutes de cross-compile sur OrbStack. Image v1.6.0 = 5,39 Go (vs 4,17 Go pour v1.4.0 — +1,2 Go = les kernels BSA).
Premier bench, config par défaut
Default chart config (héritée v1.4.4) : --cache-type-k tq3_0 --cache-type-v tq3_0 --budget 22.
Run 1: 87,45 t/s
Run 2: 89,22 t/s
Run 3: 88,09 t/s
88,25 t/s avg. Identique à hier (88,5). Dans le bruit.
Première réaction : la PR #94 ne se voit pas ? Je vérifie qu’elle est bien active. grep "SWA layers" source/safetensors_draft.cpp → la fonction est là. cat /models/draft/config.json → layer_types: [sliding × 4, full × 1], sliding_window: 2048 — exactement ce que la PR attend. La code path s’exécute.
Mais le log diagnostic [draft] SWA layers: 4/5 (window=2048) n’apparaît pas dans la sortie de test_dflash. Soit la code path s’exécute silencieusement, soit le log va sur stderr qu’on ne capture pas dans kubectl logs.
Verdict : probablement actif, mais sans gain visible ici.
Midi : config officielle de la PR
La PR #94 documente un setup spécifique pour le 106 t/s :
DFLASH27B_KV_K=q4_0 DFLASH27B_KV_V=q4_0 DFLASH27B_FA_WINDOW=0 \
python scripts/run.py --budget 10 --max-ctx 4096 ...
Trois différences vs ma config :
q4_0au lieu detq3_0pour le KV cacheFA_WINDOW=0(full attention) au lieu de 2048 (sliding window)budget=10au lieu de 22
Je patch via kubectl patch deployment (plus rapide qu’un re-bump de chart) et je lance un sweep complet : 6, 10, 14, 18, 22, 26, 30. Sept boots × 5 min + 30 s bench = ~40 min. Je laisse tourner pendant que je continue d’autres choses.
L’après-midi : le sweep complet
| Budget | Status | t/s avg |
|---|---|---|
| 6 | crash 503 1ère requête | — |
| 10 | crash 503 | — |
| 14 | crash 503 | — |
| 18 | OK | 84,8 [83,5-85,9] |
| 22 | OK ← peak | 88,7 [87,5-89,4] |
| 26 | OK puis drop | 76,7 [75,5-77,5] |
| 30 | 1 run OK puis crash 503 | 68,3 → instable |
Courbe en dôme. Peak à 22.
+0,2 t/s vs ma v1.4.4 baseline (88,5). Dans le bruit de mesure.
Trois hypothèses possibles
1. Le matched 3.6 SWA draft ne brille pas sur prompts courts
Mon prompt Space Invaders fait ~25 tokens. Les optimisations SWA (window=2048) sur le drafter ne se manifestent que quand la séquence du draft dépasse la fenêtre. À 25 tokens, le drafter voit tout, comme un layer full-attention normal.
Le sweep RTX 4090 de la PR #94 a probablement utilisé bench_he.py qui run HumanEval (prompts plus longs, code complet à compléter). Plus en accord avec là où SWA gagne.
Pour valider, faudrait re-tester avec un prompt 8K-16K tokens. Mais notre Space Invaders bench est notre standard depuis le début, et c’est révélateur du chat normal.
2. Le sweet spot diffère par architecture GPU
PR #94 sur RTX 4090 (Ada sm_89) = sweet spot budget 10, max 106 t/s. Mon Olares One RTX 5090M (Blackwell sm_120) = sweet spot budget 22, max 88,7 t/s.
Différence radicale. Et budget plus haut → batch plus large → bandwidth plus consommée. Sur 5090M sm_120 mobile, on a 75 % de la bandwidth d’une 4090 mais seulement la moitié des SMs — la fenêtre où le verify batch tient sans crasher est différente.
L’hypothèse plus probable : avec q4_0 KV cache, la mémoire-par-token est plus dense. Budget faible (6, 10, 14) → trop de fragmentation et le daemon OOM dès la première requête (503). Budget 22 = sweet spot où on a assez de mémoire pour le verify batch sans crasher. Budget 26+ = verify domine l’overhead, drop.
C’est cohérent avec le fait que mes budgets 6/10/14 crashent 503 alors que le RTX 4090 du PR les gère. Le 4090 desktop a plus de free VRAM après modèle + drafter (probablement parce qu’il n’a pas HAMi vGPU en plus).
3. La PR #94 est en cours, peut-être pas finalisée
PR #94 reste OPEN (4 mai). Pas encore mergée. Le fait que mon test ne reproduise pas le 106 t/s pourrait simplement vouloir dire que la branche n’est pas encore à son état final. Sandropuppo a peut-être des commits supplémentaires en local pas encore push.
À surveiller. Si la PR merge avec un fix qui change la donne sur sm_120 spécifiquement, je rebuild.
Le verdict honnête de la journée
Plateau à ~88 t/s sur Qwen3.6-27B / RTX 5090M / 24 Go. Trois stacks distincts y arrivent, à 0,5 t/s près :
| Stack | t/s avg |
|---|---|
| vLLM Turbo (Genesis + TurboQuant K8V4 + MTP n=3) | 88,0 |
| Lucebox v1.4.4 (tq3_0 KV + DDTree 22) | 88,5 |
| Lucebox v1.6.0 (PR #94 + q4_0 KV + DDTree 22) | 88,7 |
| buun-llama-cpp DFlash + Q8_0 drafter | 80 |
| llama.cpp standard (sans spec) | 33-36 |
Pour breakthrough au-delà de 90 t/s, plusieurs voies upstream à attendre :
- llama.cpp PR #22673 (am17an, MTP support) : open, en review, communauté Reddit mesure +75 % sur Strix Halo. Sur Blackwell estimé ~70 t/s vs 33-36 baseline.
- vLLM PR #41123 (TurboQuant 2-bit hybrid) : open, débloque KV compression plus aggressive sur Qwen3.6 hybrid.
- Qwen FlashQLA sm_120 : leur lib annonce 2-3× sur les GDN kernels Qwen3.6. Sm_120 release “soon” per maintainer.
- Quantized DFlash drafter dans une distribution autre que spiritbuun GGUF — par exemple à partir d’AutoRound INT4 si une équipe le porte.
Aucune de ces voies n’est actionnable aujourd’hui. 88 t/s est notre cap pratique sur ce hardware fin Q2 2026.
La leçon de cet épisode
Pas tous les benchmarks upstream se reproduisent sur n’importe quel hardware. PR #94 sur RTX 4090 desktop 24 Go = +57 %. Sur RTX 5090M Blackwell mobile 24 Go = +0,2 %. Même VRAM, même modèle, même quantization — différence d’architecture GPU et de configuration mémoire suffit à invalider le claim.
Et la transparence sur les résultats négatifs est aussi utile que la transparence sur les positifs. Quelqu’un d’autre va lire la PR #94, voir 106 t/s annoncé, tenter de reproduire sur son 5090. Ce post lui économise un build de 2 h et lui dit où regarder.
Prochaines étapes
- Filer un commentaire sur PR #94 avec mes chiffres Olares pour que sandropuppo voit que sm_120 ne reproduit pas leur 106 t/s
- Quand PR #22673 (llama.cpp MTP) merge upstream → rebuild buun fork avec ce commit + bench
- Tester PFlash sur prompt long (le seul path où la PR #94 BSA build flag pourrait briller)
Voilà ! Si quelqu’un sur 5090M ou autre Blackwell consumer 24 Go reproduit ces chiffres ou trouve le déblocage qui m’échappe, je veux savoir comment.
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.