Kövesd a gyártói doksit, és háromszor lassabb leszel
Két gyártó, ugyanaz a gép, ellentétes ajánlás — 10 mérési pont, 0 keresztezés
Van egy DGX Sparkod. Letöltöd a Qwen3.6-35B-A3B-t NVFP4-ben, elolvasod a model cardot, bemásolod az indítási parancsot. Kész.
Csak épp három NVFP4-kvantálás létezik ugyanabból a modellből, két különböző szervezettől, és a két kártya egymásnak ellentmondó indítási parancsot ad ugyanarra a hardverre. Nem árnyalatnyi eltérés: az egyik kifejezetten megtiltja azt, amit a másik előír.
Lemértük. A rossz választás ezen a gépen 2,0–3,3-szoros lassulás. Nyilvános a backend-mérés teljes mátrixa GB10-en.
A helyzet
Az NVIDIA saját NVFP4-kvantálásának model cardján van egy külön DGX Spark szekció. A javasolt indítás lényege:
--attention-backend flashinfer --moe-backend marlin
Az Unsloth ugyanennek a modellnek a saját NVFP4-kvantálásához, szintén a DGX Sparkot néven nevezve, azt írja, hogy a Marlin backendet ne használd, mert kétszer lassabb. Helyette:
--moe-backend flashinfer_b12x --linear-backend flashinfer_b12x
Ugyanaz a GB10 chip. Ugyanaz az alapmodell. Ugyanaz a kvantálási formátum. Ellentétes utasítás.
Az egyik téved. A kérdés csak az, melyik, és mennyivel.
Amit mértünk
Három NVFP4-checkpoint és a produkciós FP8-referencia, egy gépen (GB10, 128 GB unified), vLLM v0.24.0, rögzített image-digesttel:
| kulcs | méret | kvantálás | jellegzetesség |
|---|---|---|---|
nv | 21,80 GiB | modelopt_fp4 | statikus aktivációs skálák |
uns | 24,66 GiB | compressed-tensors | a 32–39. réteg expertjei FP8-on |
uns-fast | 22,01 GiB | compressed-tensors | az nv bájt-ikre |
| FP8 | 34,88 GiB | fp8 (block) | a jelenlegi produkciós modell |
Az uns és az uns-fast közti egyetlen különbség: az utolsó nyolc réteg expertjei az előbbinél FP8-ban maradtak, +2,65 GiB. Az nv és az uns-fast viszont bájtra ikrek, 30841 vs 30840 packed FP4-tenzor, ugyanaz a 180 magas precizitáson hagyott súly. Erre még visszatérünk, mert ez a cikk legkellemetlenebb adata.
A backend-sweep konfigurációja: egy modell (uns-fast), egy instance, GPU_UTIL=0.85, 262144 kontextus, max_num_seqs=16, spekulatív dekódolás kikapcsolva, 60 másodperces mérések c=1, 2, 4, 8, 16 párhuzamosságon, mindkét backenden. Minden futás után ellenőrizve a szerver-logból, hogy ténylegesen az a backend töltődött be, amit kértünk. A vLLM ugyanis csendben visszaesik másikra, és ilyenkor nem azt méred, aminek a fejlécében hiszed.
Az eredmény
| párhuzamosság | b12x (tok/s) | marlin (tok/s) | marlin előnye |
|---|---|---|---|
| c=1 | 35,3 | 70,6 | 2,0× |
| c=2 | 53,3 | 114,1 | 2,1× |
| c=4 | 67,8 | 178,8 | 2,6× |
| c=8 | 86,2 | 283,0 | 3,3× |
| c=16 | 149,7 | 383,9 | 2,6× |
Tíz mérési pont, és egyetlen olyan sincs, ahol a b12x nyerne. Nincs keresztezési pont, nincs olyan terhelési tartomány, ahol az Unsloth ajánlása megérné.
Ezt előre nem így vártam. A korábbi, gyártó-ajánlott körökben a b12x egyszer jobbnak látszott egyetlen stream mellett, de az per-token dekódolási sebesség volt, nem fenntartott átbocsátás. Amint sustained throughputot mérünk, már c=1-en is kétszeres a marlin. A „single-streamen azért nyer” mentőöv sem létezik.
Egy melléklelet: a b12x instabil is. Az első sweep c=8-on 44 tok/s-t adott, a megismételt futás 86-ot, kétszeres szórás ugyanazon a konfiguráción. A marlin ugyanitt 276 és 283, azaz 2,5%-on belül. Nem csak lassabb, hanem megbízhatatlanabb is.
Miért? — a hipotézis, ami megbukott
Volt egy tetszetős magyarázatunk. A FlashInfer autotuner indításkor „bucketeket” hangol be bizonyos tenzoralakokra, és ha a futás közben előálló batch-alakok kiesnek a behangolt tartományból, tartalék kernelre esik vissza. Az NVIDIA checkpointjának indítási logjában látszik is egy ilyen figyelmeztetés, ami szó szerint teljesítmény-szakadékot (perf cliff) emleget, és a tuning-tartomány kibővítését javasolja.
Kézenfekvő volt: c=1-en még a behangolt tartományban vagyunk, c≥4-en már nem, innen a szakadék.
Nem ez az. A teljes sweep alatt mentett szerver-logokban a szakadékra utaló minták előfordulási száma mindkét backenden nulla. A JIT-fordítási figyelmeztetések száma is azonos (5 és 5), és azok is a közös DeltaNet-kernelekhez tartoznak, nem a MoE-úthoz.
Marad a prózaibb magyarázat: a b12x kernel a B200 tenzormagjaira készült, és a GB10 sm_121a architektúrájára egyszerűen nem képződik le jól. Nem konfigurációs hiba, nem hangolási kérdés, rossz kernel a rossz chipen.
Ez fontos különbség. Ha tuning-probléma lenne, meg lehetne javítani. Így viszont az Unsloth ajánlása nem „finomhangolatlan”, hanem érvénytelen ezen a hardveren.
És akkor jön a második meglepetés
A tanulság, amit ebből az ember levonna: „használj marlint”. Ez viszont hamis általánosítás, és pont ez a cikk legfontosabb mondata.
Ugyanezt a backend-kört lefuttattuk a produkciós FP8-checkpointra is. A vLLM hat FP8 MoE-backendet kínál elvben. A valóság GB10-en:
| backend | eredmény |
|---|---|
| triton | ✅ 256,1 tok/s — a legjobb |
| marlin | ✅ 232,0 tok/s — működik, de 9%-kal rosszabb |
| deep_gemm | ❌ assert-crash sm_121a-n (és ez az alapértelmezett választás) |
| cutlass | ❌ letiltva erre a konfigurációra |
| flashinfer_cutlass | ❌ nem támogatja a block-FP8 sémát |
| flashinfer_trtllm | ❌ a kernel nem támogatja ezt az eszközt |
Hatból négy el sem indul. És a hatodik, ami elindulna alapértelmezésben, assert-tel elszáll, az FP8-modell GB10-en csak akkor jön fel egyáltalán, ha explicit kikapcsolod a deep_gemm-et.
De a lényeg a maradék kettő: NVFP4-en a marlin nyer, FP8-on a marlin veszít. Ugyanazon a gépen, ugyanazon a vLLM-verzión, ugyanabból a modellcsaládból. A helyes backend nem a hardver tulajdonsága, hanem a hardver és a kvantálási formátum párosáé.
Ez az, amit egyik model card sem mond ki, és amit nem lehet átvinni egyik modellről a másikra.
A gyártói dokumentáció mint aknamező
Ha már itt tartunk, néhány további dolog, amit a kártyák nem említenek:
Halott környezeti változók. Az NVIDIA DGX Spark-parancsában szereplő két környezeti változóra a vLLM v0.24.0 azt válaszolja, hogy nem ismeri őket: VLLM_FP8_MOE_BACKEND és VLLM_USE_FLASHINFER_MOE_FP4. Beállítod, exportálod, semmi nem történik. A parancs attól még lefut, csak épp nem azt csinálja, amit hiszel.
A spekulatív dekódolás draft-modellje külön backendet kíván. Mindhárom NVFP4-checkpoint kihagyja az MTP-modult a kvantálásból, a draft-fej BF16 marad. Ezt viszont sem a marlin, sem a b12x nem tudja betölteni, mert azok kvantált súlyokra valók. A megoldás egy külön kapcsoló a spekulatív configban:
--speculative-config={"method":"mtp","num_speculative_tokens":2,"moe_backend":"triton"}
Az NVIDIA kártyáján ez a triton ott szerepel. Hogy miért, azt nem magyarázza meg senki. Ha kihagyod, a szerver el sem indul, és a hibaüzenetből nem nyilvánvaló, hogy a draft modellről van szó.
A kvantálatlan draft-fej egyébként mérhető nyereség. Mindhárom NVFP4-változat 73–75%-os spekulációs elfogadási arányt ér el, a produkciós FP8 71%-ot. Megnéztük, miért: az FP8-checkpointban az MTP-modul 775 tenzora kvantálva van (F8_E4M3), az NVFP4-változatokban nem. A teljes precizitású draft-fej jobban jósol. Ez a fajta információ egyik kártyán sincs rajta, a súlyfájlok fejlécéből olvasható ki, GPU nélkül, két perc alatt.
Amit valóban futtass
Ha van DGX Sparkod és ezt a modellcsaládot szolgálod ki:
NVFP4-checkpointokhoz (mindháromhoz, gyártótól függetlenül):
--moe-backend=marlin
--speculative-config={"method":"mtp","num_speculative_tokens":2,"moe_backend":"triton"}
Környezet: CUTE_DSL_ARCH=sm_121a, FLASHINFER_DISABLE_VERSION_CHECK=1. A VLLM_USE_FLASHINFER_MOE_FP4 és a VLLM_FP8_MOE_BACKEND mehet a kukába.
Block-FP8 checkpointokhoz:
VLLM_USE_DEEP_GEMM=0
… és hagyd, hogy a vLLM válasszon, tritont fog, és az a helyes. A marlint itt ne erőltesd, 9%-ot veszítesz vele.
És mindkét esetben: indítás után nézd meg a logban, mi töltődött be ténylegesen. A vLLM kiírja. Ha nem az van ott, amit kértél, akkor a mérésed, vagy a produkciód, nem arról szól, amiről hiszed.
A teljes kép
Az azonos backenden (marlin) és azonos spekulatív beállítással mért végeredmény, párhuzamos terhelés mellett:
| checkpoint | aggregált átbocsátás (c=8) | egyszálú JSON-kinyerés |
|---|---|---|
| nv | 359,4 tok/s | 115,7 tok/s |
| uns-fast | 338,0 tok/s | 103,9 tok/s |
| uns | 326,1 tok/s | 99,1 tok/s |
| FP8 (triton) | 256,1 tok/s | 68,3 tok/s |
Az NVFP4 tehát +40% párhuzamos terhelésen és +69% egyszálú kinyerésen a produkciós FP8-hoz képest. Ez az összevetés gépben, vLLM-verzióban és spekulatív beállításban is tiszta: mindkettő ugyanazon a gépen, ugyanazon a motoron futott, és mindkettő a saját legjobb backendjével.
Ez a szám valós. Ami mögötte van, az viszont nem teljesen az, aminek látszik.
Amit nem tudunk megmagyarázni
Két adat, amit a szokásos modell nem ír le, és jobb kimondani, mint elhallgatni.
A bájt-ikrek nem egyformák. Az nv és az uns-fast súlytömege 1%-on belül azonos, a packed FP4-tenzorok száma 30841 és 30840. Ugyanaz a kernel, spekuláció nélkül, mégis 7,9% a különbség egyszálú dekódolásban. Sávszélességgel ezt nem lehet magyarázni: nincs mit magyarázni, azonos a beolvasandó adatmennyiség. A legvalószínűbb jelölt a kiszolgálási út: az nv statikus aktivációs skálákat használ, mindkét Unsloth-változat futásidőben számoltat. Ezt nem izoláltuk.
Fordítva ugyanez: az uns aktív expert-útja rétegenként 15,4%-kal több bájt, a sávszélesség-modell tehát −15% körüli büntetést jósolna. A mért különbség −3,0%. Négyszeres tévedés.
Vagyis: a formátumok közti ugrást (FP8 → NVFP4, 35 → 22 GiB) a súlycsökkenés rendesen megmagyarázza. A három NVFP4 közti 3–8%-os szórást nem. Ott valami más dönt, és ezzel a méréssorozattal nem tudtuk megfogni.
A vegyes terhelésű mérés instabil. A vegyes profil háromszori ismétlése 239,9, 271,2 és 292,6 tok/s-t adott, ±10% szórás ugyanazon a konfiguráción. Az egyenletes profil ehhez képest stabil. Ez azt jelenti, hogy a vegyes oszlop bármelyik két szomszédos értéke között nem lehet rangsorolni, és minden ilyen alapú állítás hibahatárral megy. A fenti táblázat ezért az egyenletes profilt mutatja.
Összefoglalva
Ha a saját gyártód dokumentációját követed, a te géped a te modelleddel három és félszer lassabb is lehet, mint amit tudna. Nem azért, mert rossz a hardver, és nem azért, mert rosszul konfiguráltad, hanem mert az ajánlás egy másik chipre készült, és senki nem mérte le a tiéden.
Négy dolog, amit érdemes hazavinni:
- A backend-választás 2–3,3-szeres tétel. Nagyobb, mint bármi más, amit ebben a méréssorozatban találtunk — nagyobb, mint a kvantálási variánsok közti különbség, nagyobb, mint a spekulatív dekódolás nyeresége.
- A helyes backend nem hordozható formátumok között. NVFP4-en marlin, block-FP8-on triton, ugyanazon a gépen.
- A gyártói ajánlás a gyártó hardverére vonatkozik, akkor is, ha a tiédet néven nevezi. Az Unsloth számai B200-on, nagy párhuzamosságon készültek. Ott lehet, hogy igazuk van. Nálad nincs.
- Indítás után ellenőrizd a logot. Ez az egyetlen hely, ahol kiderül, hogy a rendszer azt csinálja-e, amit kértél.
A méréssorozat során háromszor jósoltam meg előre, mi fog kijönni, és mindháromszor tévedtem: azt vártam, hogy lesz keresztezési pont a két backend között (nincs), hogy a tuning-bucket magyarázza a szakadékot (nem), és hogy az óvatosabb kvantálás jobban spekulál (rosszabbul). Ezért mérünk.
A méréssorozat egy GB10-alapú fejlesztői gépen készült, vLLM v0.24.0-n, rögzített image-digesttel. Minden szám hibamentes futásokból származik; a hibás futások eldobásra kerültek. A perf-mérések során mindig egyetlen modell futott a gépen. A backend-sweep 60 másodperces mérésekből áll, a mediánok egyszálú méréseknél 1 bemelegítő + 3 mért futásból. A vegyes profilú mérések ismétlési szórása 10%, ezt a szöveg jelöli, ahol releváns.