Épisode 8 — j’avais Lucebox qui tournait à 88,5 t/s sur Olares One via le hot-swap libvgpu + trois workarounds. Nouveau champion, 0,5 t/s devant vLLM Turbo. Et puis je vois passer la PR #94 sur Lucebox-hub, postée le matin même : “Support Qwen3.6-27B-DFlash draft (SWA layers) — 106 t/s on RTX 4090”. Avec un sweep DDTree budget qui montre +57% vs le drafter mismatched 3.5 (67,4 → 106,1 t/s sur RTX 4090).
Ada sm_89 → 106 t/s. Si ça scale linéairement à Blackwell sm_120 (qui a globalement plus de bandwidth), on devrait taper 120-130 t/s sur Olares.
Spoiler dès maintenant : 88,7 t/s. +0,2 vs hier. Dans le bruit de mesure. Voilà l’enquête.
La promesse PR #94
Trois changements dans la PR :
-
Matched 3.6 draft au lieu de mismatched 3.5 — le draft
z-lab/Qwen3.6-27B-DFlash(4 sliding-window-attention layers + 1 full-attention) au lieu du draft 3.5 (5 full-attention layers). Layer types lus depuisconfig.jsonsibling au 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 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)
Build : 100 min sur OrbStack cross-arch. 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 (default).
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). Ouch. La PR #94 ne se voit pas ?
Je vérifie que la PR est bien active. grep "SWA layers" source/safetensors_draft.cpp → la fonction est bien là, qui parse config.json. cat /models/draft/config.json → layer_types: [sliding × 4, full × 1], sliding_window: 2048 — exactement ce que la PR attend.
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 (on capture stdout+stderr en théorie, mais pas sûr pour les sous-processus daemon spawned par server.py).
Verdict : probablement actif mais sans gain visible ici.
Hypothèse 1 : config par défaut sub-optimale
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 cache (4-bit standard vs 3-bit TurboQuant)FA_WINDOW=0(full attention) au lieu de 2048 (sliding window)budget=10au lieu de 22
Je patch via kubectl patch deployment (au lieu de bumper la chart, plus rapide).
Et je lance un sweep complet : 6, 10, 14, 18, 22, 26, 30. Sept builds × 5 min boot + 30 s bench = ~40 min. Je laisse tourner.
Le sweep complet
| Budget | Status | t/s avg |
|---|---|---|
| 6 | crash 503 1er request | — |
| 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 mais drop | 76,7 [75,5-77,5] |
| 30 | 1 run OK puis crash 503 | 68,3 → instable |
La courbe en dôme. Peak à 22.
+0,2 t/s vs ma v1.4.4 baseline (88,5). Dans le bruit de mesure.
Trois explications possibles
1. Le matched 3.6 SWA draft ne brille pas sur prompts courts
Mon prompt Space Invaders fait ~25 tokens. SWA optimisations (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 full-attention layer normal.
Le sweep RTX 4090 du 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 de cas d’usage 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. Pourquoi ?
DDTree budget contrôle la largeur du verify batch. Plus c’est grand, plus on parallélise mais plus la mémoire requise grimpe. Blackwell consumer (5090M) a moins de tensor core resources par SM que Ada datacenter — verify trop large = bandwidth-bound. Sweet spot plus haut sur Blackwell parce que… non, c’est pas logique. Plus haut = plus de batch = plus de bandwidth nécessaire.
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 le premier request (503). Budget 22 = sweet spot où on a assez de mémoire pour le verify batch sans crasher. Budget 26+ = verify dominate l’overhead, drop.
C’est cohérent avec le fait que mes budget 6/10/14 crashent 503 alors que le RTX 4090 du PR #94 les gère. Le 4090 desktop 24 Go a plus de free VRAM après modèle + drafter (probablement parce que pas de 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 (Sandermage 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 GGUF 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 2026-Q2.
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.