Repository navigation
perf(moe): pipeline the nvfp4 prefill scale loads on geforce sm_86 / sm_89 - #735
Merged
Merged
Conversation
…sm_89 The pointer path of the Triton prefill GEMM loads the e4m3 scales one byte per thread, too small for cp.async, so every K step waits on that load and a CTA barrier. Read them as int32 words (4 scales each) on GeForce sm_86 / sm_89 so they join the pipeline. - SCALE_WORDS constexpr in `_prefill_nvfp4_moe_kernel` (off by default) - Triton prefill tuning as one record per (GPU class, cc); GeForce sm_86 / sm_89 turn scale words on, with 8 warps when K >= 2048, and sm_86 moves to BLOCK_SIZE_M 64 from 100 tokens per expert
pretor
pushed a commit
to pretor/FreeToken
that referenced
this pull request
Oct 19, 2026
… and 1k-150k benchmark results
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Triton prefill GEMM of the inline-dequant NVFP4 MoE loads its e4m3 block scales one byte per thread on the pointer path (sm_86 / sm_89, and sm_120 without TMA). One byte is too small for cp.async, so each K step issues a synchronous load, waits for it and passes the bytes through shared memory with a CTA barrier before the decode can start. On GeForce sm_86 / sm_89 (half-rate fp32-accumulate mma, 2 CTAs per SM at 235 registers) this was the largest loss after the MMA itself.
Changes
kernel/triton/nvfp4_fused_moe.py:SCALE_WORDSconstexpr (default off). The scales are read as int32 words, 4 per word, and spread over the 8 weight words each word scales; the loads are 4 bytes, so Triton pipelines them with cp.async. NeedsFAST_DECODE == 1, pointer loads andBLOCK_SIZE_KB >= 32.kernel/nvfp4_moe.py: the Triton prefill tuning becomes one_PrefillTuningrecord per (rho class, compute capability), looked up as (class, cc) then (class, None). The sm_89 pair decode and the H100 byte decode move into the table. New entries:SCALE_WORDSon BLOCK_SIZE_M 64 GEMMs with K % 64 == 0 and contiguous, 4-byte aligned scales; 8 warps when K >= 2048 (gate_up), 4 otherwise._prefill_block_m_steps).All other GPUs get the same configs as before:
prefill_configfor 18 faked GPUs (H100, H20, A100, B200, RTX PRO 6000 and RTX 5090 with and without Gluon, RTX 4090, RTX 3080 Ti, RTX 3090, RTX A6000, RTX 6000 Ada, L40S, GB10, Orin, an unknown name, ROCm) x 6 (E, top_k) x 19 token counts differs only on sm_86 GeForce at 102 - 128 tokens per expert (BLOCK_SIZE_M 32 -> 64).Results
fused_experts_nvfp4(align + gate_up + act + down + sum) with random banks in 9 checkpoint shapes (Qwen3.6-35B-A3B, Qwen3.5-122B-A10B, Qwen3.8-Flash, Gemma-4-26B-A4B, MiniMax-M2.5, GLM-4.7, GLM-5.2, GLM-5.3-Flash, MiniMax-M3) x T in {16, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384};triton.testing.do_benchmedian, main / PR / main / PR, each tree with its own extensions built in place. torch 2.14.1+cu130, Triton 3.8.0. Speedup over main (ac6a15d):T = 4096 / 8192 / 16384 (- = BLOCK_SIZE_M 32, unchanged):
On the changed points the decoded weights reach the MMA through shared memory instead of registers (Triton picks this layout once the scales come from the pipeline), so the accumulation order changes; outputs differ by at most 1 bf16 ulp. The RTX 3080 Ti also gains at T = 2048 on Gemma-4-26B-A4B (1.08x) and GLM-4.7 (1.10x).
Not measured: other GeForce sm_86 / sm_89 models (the tile tables already come from one card per architecture), T > 16384, end-to-end serving on these GPUs.
Tests
pytest tests/moe/test_nvfp4_backends.py tests/kernels/test_e4m3_compat.py tests/moe/test_fused_moe.py:FREETOKEN_DISABLE_GLUON=1), same as maintest_e4m3_compat.py::test_forced_emu_matches_native,blk_aq_y)