Skip to content
/ airelien.dev
Go back
Aurélien AMSELLEM

249 t/s sur Qwen3.6 35B-A3B MTP — le modèle plus gros qui tourne plus vite que tout ce qui est plus petit

Hier je postais que Nemotron-Labs Elastic 30B-A3B NVFP4 atteignait 166 t/s sur Olares One — puis 182 quand vLLM #40082 a atterri. Nouveau record. Titre du post : 'LLM le plus rapide sur Olares One'. Moins de 12 heures plus tard, ce record est maintenant en deuxième place. Qwen3.6 35B-A3B MTP tourne à 249 t/s sur le même hardware. Modèle plus gros, +37% plus rapide. Voici ce qui se passe.

Hier je postais que Nemotron-Labs Elastic 30B-A3B NVFP4 était le LLM le plus rapide que je pouvais faire tourner sur Olares One. 166 t/s. Puis ce matin vLLM PR #40082 (FlashInfer SM120 b12x MoE + FP4 GEMM) a atterri en nightly et ce chiffre est passé à 182 t/s. J’ai écrit dessus aussi. C’était cool. Filed away.

12 heures plus tard ce n’est plus le LLM le plus rapide que je peux faire tourner sur Olares One.

Le LLM le plus rapide que je peux faire tourner sur Olares One c’est unsloth/Qwen3.6-35B-A3B-MTP-GGUF à 249,30 t/s AVG, 86,6 % d’acceptation des drafts, 10 runs, σ ≈ 3,0.

C’est un modèle plus gros. Même hardware. +37 % de throughput.

Voici comment cette math fonctionne.

Le bench

unsloth/Qwen3.6-35B-A3B-MTP-GGUF UD-Q3_K_XL (17,2 GB sur disque) chargé dans mon image aamsellem/llama-cpp-mtp:master-ad27757 (la même que celle que j’ai shippée hier pour le path Qwen3.6 27B dense MTP — inclut #23269 cleanup, #23287 NVIDIA backend sampling, #22522 PDL).

Config :

--spec-type draft-mtp --spec-draft-n-max 3
--ctx-size 32768 --cache-type-k q4_0 --cache-type-v q4_0
--batch-size 512 --ubatch-size 512 --parallel 1 --flash-attn on
--chat-template-kwargs '{"enable_thinking": false}'

10 runs de completion Space Invaders HTML (2000 tokens chacun), single user, pas de warmup au-delà du chargement modèle :

Runt/sdraft_nacceptésaccept %
1253,731634145489,0 %
2248,631672144186,2 %
3251,651653144887,6 %
4251,751653144887,6 %
5250,431657144687,3 %
6245,621692143484,8 %
7248,141672144086,1 %
8249,721661144587,0 %
9249,781663144486,8 %
10243,581707143083,8 %

249,30 t/s AVG. Range 10,15 sur les 10 runs (pas de pénalité warmup — les graphs de draft MTP s’échauffent beaucoup plus vite que la capture full CUDA graph de vLLM).

Le nouveau leaderboard Olares One

Stackt/s AVGClasse modèle
Qwen3.6 35B-A3B MTP Q3_K_XL (llama.cpp master)249,3035B MoE-A3B + MTP
Nemotron-Labs Elastic 30B-A3B NVFP4 (vLLM + FlashInfer #40082)182,1430B MoE-A3B + NVFP4
Gemma 4 26B-A4B (vLLM tokenspeed-preview)135,9726B MoE-A4B + AWQ-INT4
BeeLlama Qwen3.6 27B + DFlash + turbo3 KV107,5427B dense + DFlash
llama.cpp master ad27757 — Qwen3.6 27B dense MTP74,2827B dense + MTP

Ce qui frappe c’est la ligne du bas. Même image, même path code MTP, même série modèle, même hardware. La variante 27B dense tourne à 74. La variante 35B-A3B tourne à 249. Le modèle plus gros est 3,4× plus rapide que le plus petit.

C’est toute l’histoire là. Ci-dessous c’est le pourquoi.

Trois multiplicateurs qui se composent

1. Le routing MoE-A3B fait que ce sont les params actifs qui pilotent le coût par token, pas les params totaux

Qwen3.6 35B-A3B a 35 milliards de paramètres en storage. À l’inférence, le router pick ~8 experts sur 128 par token, et seuls ceux-là s’activent. Total params actifs par token : roughly 3 milliards. Le compute par token est plus proche d’un modèle dense 3B que d’un dense 35B.

La variante 27B dense doit pousser ses 27 milliards de params dans le matmul pour chaque single token. La variante 35B-A3B doit en pousser ~3 milliards. C’est le terme dominant. Le router a un petit surcoût (~5 %) mais c’est noyé par les économies.

Côté qualité, t’as pas “la qualité d’un modèle 3B” — t’as “la qualité d’un modèle 35B où chaque token route vers la meilleure mixture d’experts équivalente-3B pour ce token”. La raison pour laquelle ça marche, c’est que différents tokens (math, code, prose, output structuré, etc.) finissent par router vers différents experts entraînés à se spécialiser. Le MoE t’achète de la largeur sur l’axe params sans payer de largeur sur l’axe compute.

2. MTP à 86,6 % d’acceptation donne ~3,6× tokens effectifs par étape de décodage

MTP (Multi-Token Prediction) entraîne le modèle avec des têtes auxiliaires qui prédisent non pas juste le token suivant mais les 2, 3 ou plus suivants. À l’inférence, le modèle propose un draft de N tokens par étape de décodage. La target les vérifie tous en un forward pass et accepte autant que la prédiction matche.

La math à 86,6 % d’acceptation avec n_max=3 :

Le taux d’acceptance c’est le knob clé. À 50 % accept t’aurais 2,5 tokens/étape (2,5× speedup), à 70 % t’aurais 3,1, à 86,6 % t’as 3,6. Plus haut c’est dramatiquement mieux — les diminishing returns ne sont pas encore là à 86 %.

La variante 27B dense sur la même image hit 64 % d’acceptance (avec l’ancien default n_max=5) et arrive à 74 t/s. La variante 35B-A3B hit 86 % (avec le nouveau default n_max=3 de #23269) et arrive à 249 t/s. Le saut d’acceptance 64 % → 86 % se multiplie avec les économies par-token du MoE — c’est pour ça que le modèle plus petit perd méchamment.

3. NVIDIA backend sampling (#23287) maintient la boucle de draft serrée

PR #23287 a mergé un jour avant ce bench. Elle déplace le sampling du chemin de draft du CPU host vers le backend CUDA. Chaque étape de draft impliquait avant un petit mais non-zéro roundtrip host pour sampler le prochain token de draft ; maintenant ça reste sur le GPU.

Par étape de draft les économies sont petites — peut-être quelques centaines de microsecondes. Mais à 249 t/s le modèle produit un token toutes les ~4 ms, et là-dedans il y a 3 étapes de draft. Économiser même 100 μs par étape de draft se compose en un pourcentage mesurable de throughput.

Pourquoi cette PR compte plus pour MoE-A3B que pour les modèles denses : MoE a une fréquence d’étapes de draft plus haute pour le même temps wall-clock (parce que chaque étape est plus rapide). Donc le surcoût du roundtrip host devient un coût relatif plus grand. #23287 cible spécifiquement le cas où tu fais beaucoup d’étapes de draft en succession rapide — exactement ce cas.

Contraintes / ce qui manque

Fenêtre de contexte : 32K seulement 262K full native marche (update post-publish — j’avais tort sur la limite 32K).

Après avoir publié le chiffre initial de 249 t/s à 32K, j’ai fait un sweep de contexte juste pour double-check le headroom. Turns out que 262K (le full native context du modèle) rentre et tourne essentiellement au même throughput :

Contextt/s AVGDelta vs 32K
32K249,30baseline
64K252,64+1,3 % (noise)
128K250,39+0,4 % (noise)
262K (full native)245,71-1,4 %

La math mémoire : modèle 17,2 GB + KV q4_0 @ 262K (32 layers × 262K × 2) ≈ 3,2 GB + MTP draft compute buffer ≈ 1 GB + compute général ≈ 0,5 GB ≈ 21,9 GB sur 23,42 GB utilisables. Marge confortable.

La raison pour laquelle ça marche sur un modèle aussi gros : MoE-A3B c’est majoritairement du coût-par-token, pas du coût-par-contexte. Les couches Mamba portent un coût per-token constant quelle que soit la position context, et les couches d’attention sont flash-attn donc le coût read reste gérable à 262K. Le KV cache lui-même est le coût dominant qui scale avec le contexte, et à la quantization q4_0 (4-bit par valeur) il est assez petit pour que passer de 32K à 262K n’ajoute que ~2,5 GB.

Shippé comme llamacppqwen36a3bone v1.0.2 avec CTX_SIZE=262144 par défaut. C’est maintenant à la fois le path Qwen3.6 le plus rapide ET le plus long-context sur Olares One — +130 % throughput vs le BeeLlama dense path à la même fenêtre context 262K.

Q4 pas faisable à 24 GB. La variante Q4_K_XL (22 GB sur disque) load mais OOM ensuite sur l’allocation KV cache. Q3_K_XL c’est le plus gros quant qui rentre avec MTP activé. Si t’as une carte consumer 32 GB (RTX 5090 desktop, RTX 4090 avec VRAM ridicule), essaie Q4_K_XL — devrait être un gain qualité à peut-être -5 % throughput.

Mode thinking off. Les têtes de draft MTP ont été entraînées sur des outputs sans thinking. Réactiver le mode thinking (enable_thinking: true) confuse le path de draft et fait crasher l’acceptance à ~40 %. Si ton use case est reasoning-heavy, la variante 27B dense pourrait en fait gagner en qualité même si c’est plus lent.

Pas de vision, pas de tool calling. Pure single-stream text. Pour le tool calling utilise Gemma 4 vision. Pour la vision utilise BeeLlama Qwen3.6 vision. Pour de la pure single-shot speed sur du texte : cette app.

Charge soutenue pas validée. 10 runs c’est ~3,5 min de charge soutenue. Le path Gemma 4 + DFlash sur vLLM a un cycle de dégradation connu “5-fast puis 4-slow” sur des charges plus longues. J’ai pas encore validé ça sur 35B-A3B MTP. Si tu l’utilises pour des workflows agentiques qui tournent 15+ minutes, envoie-moi un mot — j’adorerais savoir si le throughput tient.

Ce que j’ai shippé

llamacppqwen36a3bone v1.0.0 → v1.0.1 sur ma source de marché Olares. L’ancien v1.0.0 était une config non-MTP Q4_K_XL qui OOM de toute façon sur 24 GB. v1.0.1 c’est le rebuild :

Worker hash a5f581097eaf5fc14e4506eaeecf6fc8 sur orales-one-market.aamsellem.workers.dev.

Où ça nous met

Sur mon leaderboard Olares One ce soir, le gap du #1 (celui-ci) au #5 (le 27B dense avec MTP) c’est 3,4×. C’est dingue. Il y a douze mois le même hardware galérait à atteindre 30 t/s sur un modèle 7B. Ce soir je dépasse 249 sur un modèle 35B.

Ce qui a changé c’est pas le silicium. C’est :

Aucune de ces idées n’est nouvelle individuellement. C’est leur composition qui est nouvelle — et cette composition arrive sur un GPU laptop consumer 24 GB, pas dans un datacenter.

Il y a douze mois t’aurais run cette même stack à 50 t/s sur une H100 à 30k$ et appelé ça cutting-edge. Ce soir je tourne ça sur 4k$ de silicium mobile workstation à 5× cette vitesse.

Je sais pas combien de temps ce gap reste aussi grand. Probablement pas très longtemps. Mais pour ce soir, sur ce hardware, sur cette stack — c’est réel, c’est reproductible, et le chart est à un clic d’install.


Hardware : Olares One — RTX 5090M Laptop (24 GB GDDR7, sm_120 Blackwell consumer mobile), Intel Core Ultra 9 275HX 24-core, 96 GB DDR5. Image : aamsellem/llama-cpp-mtp:master-ad27757 buildée depuis ggml-org/llama.cpp master HEAD ad27757. Bench prompt : completion du jeu Space Invaders HTML, 2000 tokens, temp=0,6 top_k=20 min_p=0. Dix runs single-stream, single-user.

Share this post on:

Commentaires