Skip to content

Commit ff9bf15

Browse files
[MoE] Align Swiglu MXFP4 fused quant paths (#3123)
* [MoE] align Swiglu MXFP4 fused quant paths Remove the GPT-OSS Swiglu layout env switch in favor of GateMode, align the CSV test filter with runtime dtype selection, and restore FlyDSL Swiglu _fp4 fused quant accuracy by matching the non-fused bf16 stage1 semantics. Co-authored-by: Cursor <cursoragent@cursor.com> * [MoE] keep Swiglu MXFP4 fused quant in fp32 --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 6a82fa7 commit ff9bf15

3 files changed

Lines changed: 65 additions & 15 deletions

File tree

aiter/fused_moe.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@
2525
# Default to Opus unless CK sorting is explicitly requested.
2626
_USE_CK_MOE_SORTING = os.environ.get("AITER_USE_CK_MOE_SORTING", "0") == "1"
2727
_ACT_TYPE_DISABLED_KEY = "__ignore__"
28-
_USE_GENERIC_SWIGLU_MXFP4_LAYOUT = (
29-
os.environ.get("GPTOSS_USE_GENERIC_SWIGLU_MXFP4_LAYOUT", "0") == "1"
30-
)
3128
_SWIGLU_MXFP4_BF16_BOUND = int(os.environ.get("GPTOSS_SWIGLU_MXFP4_BF16_BOUND", "256"))
3229

3330

@@ -308,7 +305,7 @@ def fused_moe_(
308305
# a16wi4: bf16 activations, int4 weights with groupwise scale
309306
q_dtype_a = dtypes.bf16
310307
elif quant_type == QuantType.per_1x32:
311-
if activation == ActivationType.Swiglu and _USE_GENERIC_SWIGLU_MXFP4_LAYOUT:
308+
if activation == ActivationType.Swiglu and gate_mode == GateMode.SEPARATED:
312309
q_dtype_a = dtypes.bf16 if M < _SWIGLU_MXFP4_BF16_BOUND else dtypes.fp4x2
313310
elif activation == ActivationType.Swiglu or gate_mode == GateMode.INTERLEAVE:
314311
if get_gfx() != "gfx950" or M < bf16_fp8_bound:
@@ -334,6 +331,7 @@ def fused_moe_(
334331
hidden_pad,
335332
intermediate_pad,
336333
isShuffled,
334+
gate_mode,
337335
)
338336

339337
block_size_M = metadata.block_m if block_size_M is None else block_size_M
@@ -410,6 +408,7 @@ def fused_moe_(
410408
topk_weights=topk_weight,
411409
# only for flydsl dsv4
412410
swiglu_limit=swiglu_limit,
411+
gate_mode=gate_mode,
413412
)
414413

415414

@@ -819,7 +818,9 @@ def get_2stage_cfgs(
819818
hidden_pad,
820819
intermediate_pad,
821820
is_shuffled=True,
821+
gate_mode=GateMode.SEPARATED.value,
822822
):
823+
gate_mode = GateMode(gate_mode)
823824
_INDEX_COLS = [
824825
"cu_num",
825826
"token",
@@ -1160,7 +1161,7 @@ def get_block_m() -> int:
11601161
stage2_has_bias=enable_bias and is_flydsl2,
11611162
)
11621163
if (
1163-
not _USE_GENERIC_SWIGLU_MXFP4_LAYOUT
1164+
gate_mode != GateMode.SEPARATED
11641165
and dtype in [dtypes.bf16, dtypes.fp16]
11651166
and q_type == QuantType.per_1x32
11661167
and activation == ActivationType.Swiglu
@@ -1449,8 +1450,10 @@ def fused_moe_2stages(
14491450
topk_ids=None,
14501451
topk_weights=None,
14511452
swiglu_limit=0.0,
1453+
gate_mode=GateMode.SEPARATED.value,
14521454
):
14531455
quant_func = get_quant(quant_type)
1456+
gate_mode = GateMode(gate_mode)
14541457
token_num, _ = hidden_states.shape
14551458
E, model_dim, inter_dim = get_inter_dim(w1.shape, w2.shape)
14561459
dtype = moe_out.dtype
@@ -1472,6 +1475,7 @@ def fused_moe_2stages(
14721475
hidden_pad,
14731476
intermediate_pad,
14741477
is_shuffled,
1478+
gate_mode,
14751479
)
14761480
if (
14771481
quant_type == QuantType.per_1x32

aiter/ops/flydsl/kernels/silu_and_mul_fq.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,13 @@ def _f32_to_e2m1(qx_f32):
297297
)
298298
gate = g
299299
linear = u
300-
301-
t = g * neg_log2e
302-
303-
if const_expr(swiglu_limit != 0 and act != "swiglu"):
300+
t = gate * neg_log2e
301+
if const_expr(act == "swiglu"):
302+
gate = arith.minimumf(gate, _limit)
303+
linear = arith.minimumf(linear, _limit)
304+
linear = arith.maximumf(linear, _neg_limit)
305+
t = gate * swiglu_neg_alpha_log2e
306+
elif const_expr(swiglu_limit != 0 and act != "swiglu"):
304307
gate = arith.minimumf(gate, _limit)
305308
linear = arith.minimumf(linear, _limit)
306309
linear = arith.maximumf(linear, _neg_limit)
@@ -314,9 +317,10 @@ def _f32_to_e2m1(qx_f32):
314317
f32, "llvm.amdgcn.rcp.f32", [den], [], []
315318
)
316319
if const_expr(act == "swiglu"):
317-
act_vals.append(gate * sig * (linear + c1_f32))
320+
act_v = gate * sig * (linear + c1_f32)
318321
else:
319-
act_vals.append(gate * sig * linear)
322+
act_v = gate * sig * linear
323+
act_vals.append(act_v)
320324

321325
if const_expr(_need_quant):
322326
local_max = c0_f32

op_tests/test_moe_2stage.py

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
from aiter.fused_moe import (
2323
fused_topk,
2424
fused_moe,
25+
get_2stage_cfgs,
26+
get_padded_M,
2527
torch_moe_stage1,
2628
torch_moe_stage2,
2729
)
@@ -244,13 +246,47 @@ def weight_per_128x128_quant(weight, quant_dtype):
244246
w2_scale_aiter = fp4_utils.e8m0_shuffle(w2_scale)
245247

246248
# # ######################## stage 1 start ###########
249+
stage1_ref_dtype = dtype
250+
if (
251+
actType == aiter.ActivationType.Swiglu
252+
and qType == aiter.QuantType.per_1x32
253+
and WQDType == dtypes.fp4x2
254+
):
255+
runtime_aq_dtype = _runtime_swiglu_mxfp4_q_dtype_a(
256+
token, actType, gateMode, qType, AQDType, WQDType
257+
)
258+
if runtime_aq_dtype == dtypes.fp4x2:
259+
metadata = get_2stage_cfgs(
260+
get_padded_M(token),
261+
model_dim,
262+
inter_dim,
263+
E,
264+
topk,
265+
dtype,
266+
runtime_aq_dtype,
267+
WQDType,
268+
qType,
269+
w1.shape[1] == (inter_dim * 2),
270+
actType,
271+
doweight_stage1,
272+
hidden_pad,
273+
intermediate_pad,
274+
getattr(w1_qt_aiter, "is_shuffled", False)
275+
or getattr(w2_qt_aiter, "is_shuffled", False),
276+
gateMode,
277+
)
278+
if metadata.fuse_quant == "fp4":
279+
# Fused Swiglu MXFP4 quantizes the f32 activation directly.
280+
# Keep the torch reference at f32 until the quantization step.
281+
stage1_ref_dtype = dtypes.fp32
282+
247283
out1_ref = torch_moe_stage1(
248284
a1_qt,
249285
w1_qt,
250286
w2_qt,
251287
topk_weights,
252288
topk_ids,
253-
dtype=dtype,
289+
dtype=stage1_ref_dtype,
254290
activation=actType,
255291
quant_type=qType,
256292
a1_scale=a1_scale,
@@ -544,6 +580,8 @@ def _row_to_kwargs(row):
544580
aq_dtype = _str2dtype(row["q_dtype_a"])
545581
wq_dtype = _str2dtype(row["q_dtype_w"])
546582
act_type = _str2enum(row["act_type"], aiter.ActivationType)
583+
# Tuned CSV rows do not carry gate mode explicitly. Infer the runtime mode
584+
# from the selected activation/weight dtype layout used by fused_moe.
547585
gate_mode = _effective_gate_mode(aq_dtype, wq_dtype)
548586
return dict(
549587
dtype=_str2dtype(row["dtype"]),
@@ -594,6 +632,7 @@ def _iter_csv_cases():
594632
expected_aq_dtype = _runtime_swiglu_mxfp4_q_dtype_a(
595633
kwargs["token"],
596634
kwargs["actType"],
635+
kwargs["gateMode"],
597636
kwargs["qType"],
598637
kwargs["AQDType"],
599638
kwargs["WQDType"],
@@ -634,7 +673,9 @@ def _effective_swiglu_limit(quant_type, aq_dtype, wq_dtype, swiglu_limit):
634673
return 0.0
635674

636675

637-
def _runtime_swiglu_mxfp4_q_dtype_a(token, act_type, q_type, aq_dtype, wq_dtype):
676+
def _runtime_swiglu_mxfp4_q_dtype_a(
677+
token, act_type, gate_mode, q_type, aq_dtype, wq_dtype
678+
):
638679
"""Return the q_dtype_a that fused_moe will select for Swiglu MXFP4."""
639680
if act_type != aiter.ActivationType.Swiglu:
640681
return None
@@ -643,11 +684,12 @@ def _runtime_swiglu_mxfp4_q_dtype_a(token, act_type, q_type, aq_dtype, wq_dtype)
643684
if aq_dtype not in [dtypes.bf16, dtypes.fp16, dtypes.fp8, dtypes.fp4x2]:
644685
return None
645686

646-
if os.environ.get("GPTOSS_USE_GENERIC_SWIGLU_MXFP4_LAYOUT", "0") == "1":
687+
gate_mode = GateMode(gate_mode)
688+
if gate_mode == GateMode.SEPARATED:
647689
bound = int(os.environ.get("GPTOSS_SWIGLU_MXFP4_BF16_BOUND", "256"))
648690
return dtypes.bf16 if token < bound else dtypes.fp4x2
649691

650-
bound = int(os.environ.get("AITER_BF16_FP8_BOUND", "512"))
692+
bound = int(os.environ.get("AITER_BF16_FP8_MOE_BOUND", "256"))
651693
return dtypes.bf16 if get_gfx() != "gfx950" or token < bound else dtypes.fp8
652694

653695

0 commit comments

Comments
 (0)