-
Notifications
You must be signed in to change notification settings - Fork 1.5k
fix: make the cutlass MoE gemm profiler MXFP8-aware (autotune crash on MXFP8xMXFP8) #3614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6dfbdb2
ddeb61d
9a553af
054ec99
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ | |
| */ | ||
|
|
||
| #pragma once | ||
| #include <algorithm> | ||
| #include <cstdint> | ||
|
|
||
| #include "cutlass/gemm/gemm.h" | ||
|
|
@@ -635,7 +636,8 @@ class CutlassMoeFCRunner : public CutlassMoeFCRunnerInterface { | |
| } | ||
|
|
||
| std::vector<cutlass_extensions::CutlassGemmConfig> getTactics(MoeGemmId gemm_id) override { | ||
| return moe_gemm_runner_.getConfigs(gemm_id == MoeGemmId::GEMM_2 && mayHaveFinalizeFused()); | ||
| return filterMxfp8Tactics( | ||
| moe_gemm_runner_.getConfigs(gemm_id == MoeGemmId::GEMM_2 && mayHaveFinalizeFused())); | ||
| } | ||
|
|
||
| int queryOccupancyForConfig(cutlass_extensions::CutlassGemmConfig const& config) override { | ||
|
|
@@ -644,8 +646,25 @@ class CutlassMoeFCRunner : public CutlassMoeFCRunnerInterface { | |
|
|
||
| static std::vector<cutlass_extensions::CutlassGemmConfig> getTactics(int sm, MoeGemmId gemm_id) { | ||
| using RunnerType = decltype(moe_gemm_runner_); | ||
| return RunnerType::getConfigs(sm, | ||
| gemm_id == MoeGemmId::GEMM_2 && Self::mayHaveFinalizeFused(sm)); | ||
| return filterMxfp8Tactics( | ||
| RunnerType::getConfigs(sm, gemm_id == MoeGemmId::GEMM_2 && Self::mayHaveFinalizeFused(sm))); | ||
| } | ||
|
|
||
| // MXFP8 shares the FP8 activation/weight types, so the underlying gemm | ||
| // runner also reports the non-TMA (SM80/SM89-style) fallback configs. | ||
| // Those paths cannot do FpX block scaling (they hard-assert | ||
| // !use_block_scaling), so they must never be offered as tactics for an | ||
| // MXFP8 runner instantiation. | ||
| static std::vector<cutlass_extensions::CutlassGemmConfig> filterMxfp8Tactics( | ||
| std::vector<cutlass_extensions::CutlassGemmConfig> configs) { | ||
| if constexpr (use_mxfp8) { | ||
| configs.erase(std::remove_if(configs.begin(), configs.end(), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The use of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in ddeb61d — explicit |
||
| [](cutlass_extensions::CutlassGemmConfig const& config) { | ||
| return !config.is_tma_warp_specialized; | ||
| }), | ||
| configs.end()); | ||
| } | ||
| return configs; | ||
| } | ||
|
|
||
| void runMoe(void const* input_activations, void const* input_sf, bool const swizzled_input_sf, | ||
|
|
@@ -1011,7 +1030,8 @@ struct GemmProfilerBackend { | |
| int num_experts, int k, int64_t hidden_size, int64_t unpadded_hidden_size, | ||
| int64_t inter_size, int64_t group_size, ActivationType activation_type, bool bias, | ||
| bool use_lora, bool min_latency_mode, bool need_weights, | ||
| MOEParallelismConfig parallelism_config, bool const enable_alltoall) { | ||
| MOEParallelismConfig parallelism_config, bool const enable_alltoall, | ||
| bool use_mxfp8_act_scaling = false) { | ||
| mInterface = &runner; | ||
| mGemmToProfile = gemm_to_profile; | ||
| mDType = dtype; | ||
|
|
@@ -1040,6 +1060,14 @@ struct GemmProfilerBackend { | |
| } else if ((dtype == nvinfer1::DataType::kFP4 || dtype == nvinfer1::DataType::kINT64) && | ||
| (wtype == nvinfer1::DataType::kFP4 || wtype == nvinfer1::DataType::kINT64)) { | ||
| mScalingType = TmaWarpSpecializedGroupedGemmInput::FpXBlockScalingType::NVFP4; | ||
| } else if (use_mxfp8_act_scaling && dtype == nvinfer1::DataType::kFP8 && | ||
| wtype == nvinfer1::DataType::kFP8) { | ||
| // MXFP8xMXFP8: same storage dtypes as plain FP8, so the data types | ||
| // alone cannot identify it. Without this the profiler prepares | ||
| // per-tensor FP8 quant params and a null activation-SF buffer, and the | ||
| // TMA warp-specialized MXFP8 kernels fault on the null SF descriptor | ||
| // during the tuning pass (issue #3558). | ||
| mScalingType = TmaWarpSpecializedGroupedGemmInput::FpXBlockScalingType::MXFPX; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -1558,6 +1558,10 @@ def test_moe_mxfp8_mxfp4( | |||
| @pytest.mark.parametrize( | ||||
| ("alpha", "beta", "limit"), [(None, None, None), (0.5, 0.0, 7.0), (1.702, 1.0, 7.0)] | ||||
| ) | ||||
| # use_autotune=True is the regression coverage for issue #3558: the gemm | ||||
| # profiler used to prepare per-tensor FP8 quant params and a null | ||||
| # activation-SF buffer for MXFP8xMXFP8, crashing the tuning pass. | ||||
| @pytest.mark.parametrize("use_autotune", [False, True]) | ||||
| @pytest.mark.skipif( | ||||
| torch.cuda.get_device_capability()[0] not in [10], | ||||
| reason="MXFP8xMXFP8 is only supported on SM100 for now", | ||||
|
|
@@ -1572,6 +1576,7 @@ def test_moe_mxfp8_mxfp8( | |||
| alpha, | ||||
| beta, | ||||
| limit, | ||||
| use_autotune, | ||||
| ): | ||||
| """Test MoE with MXFP8 activations and MXFP8 weights.""" | ||||
| if top_k > num_experts: | ||||
|
|
@@ -1617,21 +1622,22 @@ def test_moe_mxfp8_mxfp8( | |||
| limit_t = None | ||||
| beta_t = None | ||||
|
|
||||
| _ = fused_moe.cutlass_fused_moe( | ||||
| mxfp8_x, | ||||
| selected_experts.to(torch.int), | ||||
| routing_weights, | ||||
| mxfp8_w1.contiguous(), | ||||
| mxfp8_w2.contiguous(), | ||||
| otype, | ||||
| swiglu_alpha=alpha_t, | ||||
| swiglu_limit=limit_t, | ||||
| swiglu_beta=beta_t, | ||||
| quant_scales=quant_scales, | ||||
| input_sf=mxfp8_x_sf, | ||||
| use_mxfp8_act_scaling=True, | ||||
| output=flash_output, | ||||
| ) | ||||
| with autotune(True) if use_autotune else nullcontext(): | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. skip_ops is also available for this purpose flashinfer/flashinfer/autotuner.py Line 523 in af979cc
cc @qiching
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good pointer, thanks - kept the parametrized context here since the crash lived in the profiling path itself, so the test wants autotune fully on/off per case, but skip_ops is the right tool when only a specific op should stay on heuristics. |
||||
| _ = fused_moe.cutlass_fused_moe( | ||||
| mxfp8_x, | ||||
| selected_experts.to(torch.int), | ||||
| routing_weights, | ||||
| mxfp8_w1.contiguous(), | ||||
| mxfp8_w2.contiguous(), | ||||
| otype, | ||||
| swiglu_alpha=alpha_t, | ||||
| swiglu_limit=limit_t, | ||||
| swiglu_beta=beta_t, | ||||
| quant_scales=quant_scales, | ||||
| input_sf=mxfp8_x_sf, | ||||
| use_mxfp8_act_scaling=True, | ||||
| output=flash_output, | ||||
| ) | ||||
|
|
||||
| dq_mxfp8_x = ( | ||||
| mxfp8_dequantize_host( | ||||
|
|
||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To ensure type safety and consistency with
prepareQuantParams(wherequant_2andquant_5are cast toTmaWarpSpecializedGroupedGemmInput::MXFPXElementSF const*), it is safer to usesizeof(TmaWarpSpecializedGroupedGemmInput::MXFPXElementSF)instead ofsizeof(TmaWarpSpecializedGroupedGemmInput::ElementSF)when calculatingquant_2_sizeandquant_5_size.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in ddeb61d — both sizeofs now use
MXFPXElementSFfor consistency with theprepareQuantParamscasts (currently an alias ofElementSF, but this keeps the branch type-coherent if they ever diverge). Recompiled + reran the FP8 no-regression check, bit-identical.