Skip to content

Commit 90de246

Browse files
rootcursoragent
authored andcommitted
[FlyDSL] Add HY4 MXFP8 heterogeneous MoE fusion
Fuse HY4's FP8 shared expert into the routed MXFP8 MoE kernels to reduce launch and scheduling overhead, with AOT coverage, correctness guards, and reproducible gfx950 tuning. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent ab96851 commit 90de246

16 files changed

Lines changed: 2491 additions & 94 deletions

File tree

‎.github/scripts/split_tests.sh‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ if [[ "$TEST_TYPE" == "aiter" ]]; then
5656
printf '%s\n' \
5757
"$TEST_DIR/tuning_tests/test_csv_validation.py" \
5858
"$TEST_DIR/tuning_tests/test_config_shape_collision.py" \
59+
"$TEST_DIR/tuning_tests/test_fhmoe_tuner.py" \
5960
"$TEST_DIR/tuning_tests/test_mixed_mxfp_tuning.py"
6061
} | LC_ALL=C sort -u
6162
)
@@ -182,6 +183,7 @@ if [[ "$TEST_TYPE" == "aiter" ]]; then
182183
FILE_TIMES[op_tests/test_quant_mxfp6_gemm.py]=6
183184
FILE_TIMES[op_tests/tuning_tests/test_config_shape_collision.py]=4
184185
FILE_TIMES[op_tests/tuning_tests/test_csv_validation.py]=4
186+
FILE_TIMES[op_tests/tuning_tests/test_fhmoe_tuner.py]=4
185187
FILE_TIMES[op_tests/tuning_tests/test_mixed_mxfp_tuning.py]=4
186188
FILE_TIMES[op_tests/test_fused_qk_rmsnorm_per_token_quant.py]=5
187189
FILE_TIMES[op_tests/test_groupnorm.py]=5
@@ -196,7 +198,7 @@ if [[ "$TEST_TYPE" == "aiter" ]]; then
196198
FILE_TIMES[op_tests/test_asm_guard.py]=4
197199
FILE_TIMES[op_tests/test_dsv4_rotate_quant.py]=4
198200
FILE_TIMES[op_tests/test_f4gemm.py]=4
199-
FILE_TIMES[op_tests/test_fhmoe.py]=4
201+
FILE_TIMES[op_tests/test_fhmoe.py]=40
200202
FILE_TIMES[op_tests/test_flydsl_batched_gemm.py]=4
201203
FILE_TIMES[op_tests/test_flydsl_fmha.py]=4
202204
FILE_TIMES[op_tests/test_fmha_fwd_mxfp8_asm.py]=4

‎.github/workflows/tuning-tests.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ jobs:
9393
-e PYTHONUNBUFFERED=1 \
9494
-w /workspace \
9595
tuning_tests_level01 \
96-
bash -lc "set -euo pipefail; python3 -m unittest op_tests.tuning_tests.test_csv_validation op_tests.tuning_tests.test_tuner_infra op_tests.tuning_tests.test_mp_tuner_logic op_tests.tuning_tests.test_config_shape_collision op_tests.tuning_tests.test_a6w6_tuning op_tests.tuning_tests.test_mixed_mxfp_tuning -v 2>&1 | tee /workspace/${LOG_DIR}/level01.log"
96+
bash -lc "set -euo pipefail; python3 -m unittest op_tests.tuning_tests.test_csv_validation op_tests.tuning_tests.test_tuner_infra op_tests.tuning_tests.test_mp_tuner_logic op_tests.tuning_tests.test_config_shape_collision op_tests.tuning_tests.test_a6w6_tuning op_tests.tuning_tests.test_mixed_mxfp_tuning -v 2>&1 | tee /workspace/${LOG_DIR}/level01.log; python3 -m pytest op_tests/tuning_tests/test_fhmoe_tuner.py -v 2>&1 | tee -a /workspace/${LOG_DIR}/level01.log"
9797
9898
- name: Upload level 0+1 artifacts
9999
if: always()

‎aiter/aot/flydsl/fhmoe.py‎

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99

1010
from dataclasses import dataclass
1111

12-
from aiter.aot.flydsl.common import cu_num_to_arch
13-
1412

1513
def _shared_weight(device, n_in: int, k_in: int):
1614
import torch
@@ -127,11 +125,25 @@ def build_stage2_args(
127125
)
128126

129127
def compile_stage1(self, **kwargs):
128+
from aiter.fhmoe import _is_hy4_mxfp8_fhmoe_contract
130129
from aiter.ops.flydsl.fhmoe import compile_flydsl_fhmoe_stage1
131130

131+
is_hy4 = _is_hy4_mxfp8_fhmoe_contract(
132+
model_dim=kwargs["model_dim"],
133+
inter_dim=kwargs["inter_dim"],
134+
experts=kwargs["experts"],
135+
topk=kwargs["topk"],
136+
routed_mxfp8=kwargs["b_dtype"] == "fp8",
137+
hidden_pad=kwargs.get("model_dim_pad", 0),
138+
intermediate_pad=kwargs.get("inter_dim_pad", 0),
139+
gate_interleaved=kwargs.get("gate_mode", "separated") == "interleave",
140+
doweight_stage1=kwargs.get("doweight_stage1", False),
141+
shared_expert_id=self.shared_expert_id,
142+
)
132143
return compile_flydsl_fhmoe_stage1(
133144
**kwargs,
134145
shared_expert_id=self.shared_expert_id,
146+
clamp_shared=not is_hy4,
135147
)
136148

137149
def compile_stage2(self, **kwargs):
@@ -145,6 +157,7 @@ def compile_stage2(self, **kwargs):
145157

146158
def precompile_fhmoe_to_cache(
147159
*,
160+
stage: int,
148161
experts: int,
149162
shared_expert_id: int,
150163
a_dtype: str = "fp8",
@@ -160,21 +173,30 @@ def precompile_fhmoe_to_cache(
160173
"FHMoE AOT expects the shared expert to be the final logical expert; "
161174
f"got {shared_expert_id=} for {experts=}"
162175
)
163-
if a_dtype != "fp8" or b_dtype != "fp4":
176+
if a_dtype != "fp8" or b_dtype not in ("fp4", "fp8"):
164177
raise ValueError(
165-
"FHMoE AOT supports routed FP8 activations and MXFP4 weights; "
178+
"FHMoE AOT supports routed FP8 activations with MXFP4 or MXFP8 weights; "
166179
f"got {a_dtype=} and {b_dtype=}"
167180
)
181+
gate_mode = kwargs.get("gate_mode", "separated")
182+
if stage == 1 and b_dtype == "fp8" and gate_mode != "interleave":
183+
raise ValueError(
184+
"FHMoE AOT requires interleaved gate/up layout for MXFP8 routed "
185+
f"weights, got {gate_mode=}"
186+
)
168187
if enable_bias:
169188
raise ValueError("FHMoE AOT does not support expert bias")
170189
if act != "silu":
171190
raise ValueError(f"FHMoE AOT supports only SiLU, got {act=}")
172-
if cu_num_to_arch(cu_num) != "gfx950":
173-
raise ValueError(f"FHMoE AOT supports only gfx950, got {cu_num=}")
191+
if cu_num not in (0, 256):
192+
raise ValueError(
193+
f"FHMoE AOT supports only gfx950 or the default CU sentinel, got {cu_num=}"
194+
)
174195

175196
from aiter.aot.flydsl.moe import _precompile_to_cache
176197

177198
return _precompile_to_cache(
199+
stage=stage,
178200
experts=experts,
179201
a_dtype=a_dtype,
180202
b_dtype=b_dtype,

‎aiter/aot/flydsl/moe.py‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@
6565
MOE_AOT_ARCH_DEFAULT = "gfx950"
6666

6767

68+
def _mx_w2_scale_numel(experts: int, model_dim: int, inter_dim: int) -> int:
69+
scale_k = (inter_dim + 255) // 256 * 256
70+
return experts * model_dim * (scale_k // 32)
71+
72+
6873
def parse_csv(csv_path: str):
6974
"""Parse the CSV and return a list of unique compile jobs.
7075
@@ -90,6 +95,8 @@ def parse_csv(csv_path: str):
9095
cu_num = int(row.get("cu_num", "0"))
9196
block_m = int(row.get("block_m", "0") or "0")
9297
shared_expert_id = int(row.get("shared_expert_id", "-1") or "-1")
98+
model_dim_pad = int(row.get("hidden_pad", "0") or "0")
99+
inter_dim_pad = int(row.get("intermediate_pad", "0") or "0")
93100
act_type = row.get("act_type", "")
94101
act_name = act_type.strip().split(".")[-1].lower()
95102
act = act_name if act_name in ("swiglu", "situv2") else "silu"
@@ -181,6 +188,8 @@ def parse_csv(csv_path: str):
181188
"enable_bias": enable_bias,
182189
"token_num": token,
183190
"block_m": block_m,
191+
"model_dim_pad": model_dim_pad,
192+
"inter_dim_pad": inter_dim_pad,
184193
}
185194
if shared_expert_id >= 0:
186195
job["shared_expert_id"] = shared_expert_id
@@ -239,6 +248,8 @@ def _precompile_to_cache(
239248
cu_num: int = 0,
240249
token_num: int = 0,
241250
block_m: int = 0,
251+
model_dim_pad: int = 0,
252+
inter_dim_pad: int = 0,
242253
a_scale_one: bool = False,
243254
xcd_swizzle: int = 0,
244255
enable_bias: bool = False,
@@ -324,7 +335,7 @@ def _make_sorted_weights(doweight: bool):
324335
return None
325336

326337
def _make_a1_scale():
327-
"""Mirror fused_moe_2stages a1_scale construction (per_1x32 + fp4-weight path)."""
338+
"""Mirror per-1x32 MXFP4/MXFP8 runtime a1_scale construction."""
328339
if not use_mx_gemm:
329340
return None
330341
if a_dtype == "fp8":
@@ -400,7 +411,7 @@ def _make_a2_scale_for_stage2():
400411
return None
401412

402413
def _make_w_scale(scale_storage_numel: int):
403-
# mxfp4 e8m0 scale -- viewed as uint8 by _view_safe before kernel launch.
414+
# MX E8M0 scale storage is viewed as uint8 before kernel launch.
404415
return torch.zeros(scale_storage_numel, dtype=torch.uint8, device=dev)
405416

406417
def _make_a_user(a_dtype_user_shape):
@@ -619,6 +630,8 @@ def _make_a_user(a_dtype_user_shape):
619630
waves_per_eu=waves_per_eu,
620631
b_nt=b_nt,
621632
gate_mode=gate_mode,
633+
model_dim_pad=model_dim_pad,
634+
inter_dim_pad=inter_dim_pad,
622635
enable_bias=(kernel_bias is not None),
623636
a_scale_one=a_scale_one,
624637
xcd_swizzle=xcd_swizzle,
@@ -682,7 +695,7 @@ def _make_a_user(a_dtype_user_shape):
682695

683696
a2_scale = _make_a2_scale_for_stage2()
684697
if use_mx_gemm:
685-
w2_scale = _make_w_scale(E * model_dim * (inter_dim // 32))
698+
w2_scale = _make_w_scale(_mx_w2_scale_numel(E, model_dim, inter_dim))
686699
else:
687700
w2_scale = torch.zeros(1, device=dev, dtype=torch.float32)
688701

@@ -810,6 +823,8 @@ def _make_a_user(a_dtype_user_shape):
810823
use_global_a=requires_flydsl_stage2_global_a(a),
811824
cu_num_mul=cu_num_mul,
812825
b_nt=b_nt,
826+
model_dim_pad=model_dim_pad,
827+
inter_dim_pad=inter_dim_pad,
813828
xcd_swizzle=xcd_swizzle,
814829
enable_bias=enable_bias,
815830
)

‎aiter/configs/tuned_fhmoe.csv‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,20 @@ gfx950,256,256,7168,384,385,7,384,ActivationType.Silu,torch.bfloat16,torch.float
1111
gfx950,256,512,7168,384,385,7,384,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float4_e2m1fn_x2,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,32,0,flydsl_moe1_afp8_wfp4_bf16_t32x128x256_w3_gui_fp8,flydsl_moe2_afp8_wfp4_bf16_t32x256x128_atomic_bnt2_persist
1212
gfx950,256,1024,7168,384,385,7,384,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float4_e2m1fn_x2,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,32,0,flydsl_moe1_afp8_wfp4_bf16_t32x128x256_w3_gui_fp8,flydsl_moe2_afp8_wfp4_bf16_t32x256x128_atomic_bnt2_persist
1313
gfx950,256,2048,7168,384,385,7,384,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float4_e2m1fn_x2,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,64,0,flydsl_moe1_afp8_wfp4_bf16_t64x128x256_w3_bnt0_gui,flydsl_moe2_afp8_wfp4_bf16_t64x128x128_atomic
14+
gfx950,256,1,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,32,0,flydsl_moe1_afp8_wfp8_bf16_t32x64x256_w4_gui_kw2_fp8,flydsl_moe2_afp8_wfp8_bf16_t32x128x128_atomic
15+
gfx950,256,2,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,32,0,flydsl_moe1_afp8_wfp8_bf16_t32x64x256_bnt0_gui_kw2_fp8,flydsl_moe2_afp8_wfp8_bf16_t32x128x128_atomic
16+
gfx950,256,4,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,32,0,flydsl_moe1_afp8_wfp8_bf16_t32x64x256_w4_bnt0_gui_fp8,flydsl_moe2_afp8_wfp8_bf16_t32x128x128_atomic
17+
gfx950,256,8,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,32,0,flydsl_moe1_afp8_wfp8_bf16_t32x64x256_w3_gui_xcd4_fp8,flydsl_moe2_afp8_wfp8_bf16_t32x128x128_atomic
18+
gfx950,256,16,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,32,0,flydsl_moe1_afp8_wfp8_bf16_t32x128x256_w3_gui_fp8,flydsl_moe2_afp8_wfp8_bf16_t32x128x128_atomic
19+
gfx950,256,32,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,32,0,flydsl_moe1_afp8_wfp8_bf16_t32x64x256_w3_gui_fp8,flydsl_moe2_afp8_wfp8_bf16_t32x128x256_atomic
20+
gfx950,256,64,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,32,0,flydsl_moe1_afp8_wfp8_bf16_t32x128x256_w3_gui_fp8,flydsl_moe2_afp8_wfp8_bf16_t32x128x128_atomic
21+
gfx950,256,128,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,32,0,flydsl_moe1_afp8_wfp8_bf16_t32x64x256_w2_gui_fp8,flydsl_moe2_afp8_wfp8_bf16_t32x128x128_atomic
22+
gfx950,256,256,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,32,0,flydsl_moe1_afp8_wfp8_bf16_t32x64x256_w3_gui_fp8,flydsl_moe2_afp8_wfp8_bf16_t32x128x128_atomic
23+
gfx950,256,512,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,32,0,flydsl_moe1_afp8_wfp8_bf16_t32x64x256_w3_gui_fp8,flydsl_moe2_afp8_wfp8_bf16_t32x128x128_atomic
24+
gfx950,256,1024,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,64,0,flydsl_moe1_afp8_wfp8_bf16_t64x128x256_gui_fp8,flydsl_moe2_afp8_wfp8_bf16_t64x128x256_atomic
25+
gfx950,256,2048,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,128,0,flydsl_moe1_afp8_wfp8_bf16_t128x128x256_gui_fp8,flydsl_moe2_afp8_wfp8_bf16_t64x128x128_atomic_persist_sbm128
26+
gfx950,256,4096,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,64,0,flydsl_moe1_afp8_wfp8_bf16_t64x128x256_w4_bnt0_gui_xcd4_fp8,flydsl_moe2_afp8_wfp8_bf16_t64x128x256_reduce
27+
gfx950,256,8192,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,128,0,flydsl_moe1_afp8_wfp8_bf16_t128x128x256_w2_bnt0_gui_xcd4_fp8,flydsl_moe2_afp8_wfp8_bf16_t128x128x128_reduce_persist
28+
gfx950,256,16384,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,128,0,flydsl_moe1_afp8_wfp8_bf16_t128x128x256_bnt0_gui_fp8,flydsl_moe2_afp8_wfp8_bf16_t128x128x128_reduce_persist
29+
gfx950,256,32768,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,128,0,flydsl_moe1_afp8_wfp8_bf16_t128x128x256_w2_bnt0_gui_xcd4_fp8,flydsl_moe2_afp8_wfp8_bf16_t64x128x128_atomic_persist_sbm128
30+
gfx950,256,131072,6144,256,257,9,256,ActivationType.Silu,torch.bfloat16,torch.float8_e4m3fn,torch.float8_e4m3fn,QuantType.per_1x32,1,0,0,0,GateMode.INTERLEAVE,128,0,flydsl_moe1_afp8_wfp8_bf16_t128x256x256_w4_bnt0_gui_fp8,flydsl_moe2_afp8_wfp8_bf16_t128x128x128_reduce_bnt2_xcd4

0 commit comments

Comments
 (0)