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

Qwen3.6-27B à 85-100 t/s sur un RTX 5090 Laptop 24 Go

J'ai adapté les recettes desktop 32 Go et Ampere 24 Go à un GPU Blackwell mobile 24 Go (sm_120). Image vLLM custom, AutoRound INT4, MTP n=3 — 85-100 t/s soutenus avec 75K de contexte.

Salut les amis ! Aujourd’hui on parle inférence locale et speculative decoding, parce que je viens de passer un week-end là-dessus et j’ai des chiffres à vous montrer.

À la base, je suis tombé sur le post de u/Kindly-Cantaloupe978 qui annonce 80 t/s à 218K de contexte et l’article Medium de Wasif Basharat à 85 t/s sur RTX 3090. Je me suis dit : si ça tourne sur 32 Go desktop et sur du 24 Go Ampere, est-ce que je peux reproduire ça sur mon Olares One — ma petite box d’IA maison avec un RTX 5090 Laptop GPU (24 Go, ~896 Go/s, sm_120 Blackwell) ? Spoiler : oui, mais il y a des pièges spécifiques au Blackwell consumer mobile qu’on ne voit dans aucun des deux articles.

Après plusieurs itérations, voilà où j’en suis : ~85-100 t/s soutenus, pic à 99.7 t/s, contexte max 75K, MTP n=3 avec 92-95 % d’acceptation une fois chaud. C’est environ 3 fois plus rapide que llama.cpp sur la même carte (33-36 t/s avec le meilleur GGUF NVFP4) et ça matche, voire ça bat, les références 32 Go desktop.

TL;DR — les chiffres

SetupHardwaret/s
llama.cpp UD-Q4_K_XL ou GGUF NVFP4RTX 5090M 24 Go33-36
vLLM v0.17 NVFP4 (sans MTP)RTX 5090M 24 Go39
vLLM v0.19.1 NVFP4 + MTP n=1RTX 5090M 24 GoOOM (modèle OK, head MTP 2.37 Gio qui ne passe pas)
vLLM 0.19.1 + Lorbus AutoRound + MTP n=1RTX 5090M 24 Go65
vLLM 0.19.1 + Lorbus AutoRound + MTP n=3RTX 5090M 24 Go85-100
Référence : même recette sur 5090 desktop 32 GoRTX 5090 32 Go78-80
Référence : la stack de Wasif sur 3090 24 GoRTX 3090 24 Go85

Cinq pièges spécifiques au Blackwell mobile 24 Go

1. NVFP4 + MTP = OOM en 24 Go

J’ai d’abord essayé sakamakismile/Qwen3.6-27B-Text-NVFP4-MTP. Sur le papier c’est parfait : NVFP4 c’est 2× le throughput FP8 sur les tensor cores Blackwell, et le nom du modèle dit qu’il inclut MTP. Le chargement passe sans broncher, et puis :

torch.OutOfMemoryError: Tried to allocate 2.37 GiB. GPU has 2.25 GiB free.

Pile le même problème que Wasif documente. Le loader Qwen3_5MTP de vLLM alloue un buffer BF16 tout neuf de 2.37 Gio pour mtp.fc parce que NVFP4 quantize tout dans le fichier. Sur 32 Go ça passe, sur 24 Go ça casse.

Le fix : passer à Lorbus/Qwen3.6-27B-int4-AutoRound, qui dé-quantize uniquement mtp.fc en BF16 directement dans le fichier (~280 Mio). vLLM le trouve sur disque, plus de buffer alloué à la volée.

Le trade-off : AutoRound INT4 utilise les kernels Marlin (tunés pour Ampere) au lieu des tensor cores NVFP4 natifs. Mais MTP n=3 apporte largement plus de vitesse que ce que l’accélération NVFP4 aurait gagné sur une carte consumer bandwidth-bound. Bref, on signe sans trembler.

2. --kv-cache-dtype fp8_e5m2 rejeté avec les checkpoints NVFP4

ValueError: fp8_e5m2 kv-cache is not supported with fp8 checkpoints.

AutoRound INT4 n’est pas dans la famille FP8, donc fp8_e5m2 passe. Bonus : ça donne plus de pool KV que fp8_e4m3 (l’Olares One se retrouve avec 23 760 tokens cachés en fp8_e5m2 + gpu-mem-util 0.97).

3. La PR vllm#36325 (Blackwell TMA fix) est obligatoire sur sm_12x

Sans elle, l’autotuner Triton OOM au warmup. is_tma_supported renvoie True pour toute compute capability ≥ 9 mais le Blackwell consumer ne fait pas vraiment de TMA — les allocations de descriptor buffer font exploser la VRAM. La PR cap à < 12. C’est un patch de 4 lignes que j’ai cherry-pické dans une image custom.

4. patch_tolist_cudagraph.py est maintenant public

Le patch précédemment privé de l’article de Wasif est maintenant dans noonghunna/qwen36-27b-single-3090/patches/. 165 lignes, qui fixent un sync CPU .tolist() qui casse la capture de CUDA graph pendant la simulation de continuation-chunk au warmup quand spec-decode + chunked-prefill se combinent. Obligatoire même avec fp8 KV (pas seulement avec TurboQuant). Merci à eux d’avoir publié !

5. MTP n=3 passe vraiment en 24 Go avec Lorbus

Je m’attendais à ce que n=3 OOM (l’article de Wasif prévient pour 24 Go avec sakamakismile). Avec le mtp.fc dé-quantizé de Lorbus et --gpu-memory-utilization 0.97, n=3 passe sans souci. La longueur d’acceptance pique à 3.86/3.0 (98 %/96 %/92 % par position), le throughput de génération pique à 99.7 t/s. Tout simplement.

La recette

Image Docker custom (FROM vllm/vllm-openai:v0.19.1-cu130) :

Args vLLM :

--model Lorbus/Qwen3.6-27B-int4-AutoRound
--quantization auto_round
--dtype float16
--attention-backend flashinfer
--kv-cache-dtype fp8_e5m2
--max-model-len 75000
--gpu-memory-utilization 0.97
--max-num-seqs 1
--max-num-batched-tokens 2048
--language-model-only
--enable-prefix-caching
--enable-chunked-prefill
--enable-auto-tool-choice
--tool-call-parser qwen3_coder
--reasoning-parser qwen3
--speculative-config '{"method":"mtp","num_speculative_tokens":3}'

Variables d’env :

VLLM_USE_FLASHINFER_SAMPLER=1
VLLM_MEMORY_PROFILER_ESTIMATE_CUDAGRAPHS=1
VLLM_ALLOW_LONG_MAX_MODEL_LEN=1
VLLM_MARLIN_USE_ATOMIC_ADD=1
VLLM_FLOAT32_MATMUL_PRECISION=high
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,max_split_size_mb:512
NCCL_CUMEM_ENABLE=0
NCCL_P2P_DISABLE=1
OMP_NUM_THREADS=1
CUDA_DEVICE_MAX_CONNECTIONS=8

Métriques en steady state

Avg generation throughput: 85-100 t/s (varie selon le contenu)
Peak : 99.7 t/s
Longueur d'acceptance moyenne : 3.20 → 3.86 (sur 3 max)
Acceptation par position : 98 % / 93 % / 88 %
Taux d'acceptation des drafts : 92-95 %
Chargement du modèle : 16.87 Gio
Pool KV : 23 760 tokens (3.24 Gio)
Utilisation KV cache pendant la génération : 21-31 %

Quelques notes en vrac

Crédits

Voilà ! Si quelqu’un veut le Dockerfile custom ou le chart Helm, faites-moi signe — je partage volontiers. Et si vous tournez sur 5090M, 4080M ou 3090 24 Go et que vous arrivez à reproduire ces chiffres (ou pas), je suis curieux d’avoir vos retours. À 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.

Share this post on:

Commentaires