[Bugfix][NVFP4 MoE] Pad gated intermediate to 64 for FlashInfer TRT-LLM shuffle (M%128) - #46880
Conversation
c4592b8 to
d8cff68
Compare
|
thanks for that, @mikekg. confirming this diagnosis independently on current
the requirement is one thing worth checking while here: the same |
10364b0 to
9f12128
Compare
9f12128 to
0074801
Compare
|
Fail is an unrelated torch.compile() fusion regression on trunk identified in |
I'd suggest putting it with a follow-up that has its own repro. I don't have a model handy to repro for FP8 activations, and I think it's a hard sell to patch a complex code base without hard data. If you can rig up a repro fail and demonstrate that it's fixed, maybe you can submit an appropriate patch? |
…LM shuffle (M%128) FlashInfer's TRT-LLM FP4 MoE weight prep shuffles block-scale rows via get_shuffle_matrix_sf_a_row_indices, which asserts the gate/up row dim is a multiple of 128 (epilogue_tile_m). align_fp4_moe_weights_for_fi pads intermediate to min_alignment and the gate/up dim is up_mult*padded_intermediate (up_mult=2 gated). min_alignment=16 for gated leaves 2*padded_intermediate a multiple of only 32, so any NVFP4 MoE whose rank-local intermediate is not 128-aligned at TP>1 (e.g. Gemma-4-26B-A4B at -tp 4) fails engine init with 'assert M % 128 == 0'. Use 64 for gated so 2*padded_intermediate is a multiple of 128; padded rows are zero so outputs are unchanged. Marlin path (vllm-project#45295) unaffected; FlashInfer TRT-LLM path only. Signed-off-by: Mike G <180722391+mikekg@users.noreply.github.com>
0074801 to
2c46d8d
Compare
Closes #46879.
Summary
FlashInfer's TRT-LLM NVFP4 MoE weight prep shuffles block-scale rows via
get_shuffle_matrix_sf_a_row_indices, which asserts the gate/up row dim is a
multiple of 128 (epilogue_tile_m). align_fp4_moe_weights_for_fi pads the
intermediate to min_alignment, and the gate/up dim is
up_multpadded_intermediate (up_mult=2 when gated). The caller passes
min_alignment = 16 if is_gated else 128; 16 is the NVFP4 scale-block size
(num_elts_per_sf=16), which aligns the quant scale blocks but is weaker than
the shuffle's 128-row tile. For gated, 2 * round_up(intermediate, 16) is only
guaranteed a multiple of 32, so a gated NVFP4 MoE whose rank-local
intermediate isn't 64-aligned at TP>1 (e.g. nvidia/Gemma-4-26B-A4B-NVFP4 at
-tp 4: 2(704/4)=352, 352%128=96) fails engine init with assert M % 128 == 0.
Why Padding to M=64 (and not the tile size requirement of M%128)
64 is not itself a shuffle tile. Gated activations compute SwiGLU(x) =
SiLU(x·W_gate) ⊗ (x·W_up), and vLLM fuses the two projections into a single
W13 = [W_gate | W_up] — one GEMM whose row dim is up_mult*padded_intermediate
with up_mult=2. The shuffle's 128-row epilogue_tile_m applies to that fused
dim, so the per-shard padded_intermediate only needs to be a multiple of 128
// up_mult = 64 for 2 * padded_intermediate to land on the tile — i.e.
min_alignment = max(16, 128 // up_mult), keeping the NVFP4 scale block (16) as
the floor. The non-gated path (up_mult=1) keeps the full 128.
Fix
min_alignment = 64 for gated, so 2 * padded_intermediate is a multiple of 128.
Padded rows are zero in both weights and scales, so outputs are unchanged.
Non-gated path (min_alignment=128) unchanged; Marlin path (#45295) unaffected.
Validation
--quantization modelopt --trust-remote-code on B200 fails at
process_weights_after_loading with assert M % 128 == 0 (FLASHINFER_TRTLLM
backend confirmed selected in the log).
padded_intermediate = round_up(176,64) = 192, so the gate/up row dim 2192 =
384 = 3128 satisfies the assert.