[ROCm][Kimi-K3] Enable a4w4 flydsl kernels for KimiK3 (#53940)
Signed-off-by: ppalanga <ppalanga@amd.com> Signed-off-by: Poovaiah Palangappa <poovaiah.palangappa@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Poovaiah Palangappa <poovaiah.palangappa@amd.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Hongxia Yang <62075498+hongxiayang@users.noreply.github.com>
ppalanga committed
Sep 15, 2026 at 18:38 UTC
a4d2d9d95edd2db39cc7dec0a3433c764ff2c578
5 files changed
+127
-30
tests/kernels/moe/test_rocm_aiter_moe.py
+68
@@ -565,6 +565,74 @@ def test_aiter_moe_shared_experts_enablement_follows_env(
565
assert rocm_aiter_ops.is_fusion_moe_shared_experts_enabled() is expected
566
567
568
+def test_aiter_moe_situv2_syncs_aiter_a4w4_env(
569
+ monkeypatch: pytest.MonkeyPatch,
570
+):
571
+ """SiTUv2 MoE should route to AITER a4w4 and clear legacy a8w4 overrides."""
572
+ from vllm._aiter_ops import rocm_aiter_ops
573
+
574
+ _assert_aiter_supported()
575
+
576
+ with monkeypatch.context() as mp:
577
+ mp.delenv("AITER_SITUV2_A8W4", raising=False)
578
+ mp.delenv("AITER_SITUV2_A4W4", raising=False)
579
+ mp.setenv("VLLM_ROCM_USE_AITER", "1")
580
+ mp.setenv("VLLM_ROCM_USE_AITER_MOE", "1")
581
+ mp.setenv("VLLM_ROCM_USE_AITER_MOE_SITUV2", "1")
582
+ _reload_envs()
583
+ rocm_aiter_ops.refresh_env_variables()
584
+
585
+ import os
586
+
587
+ assert os.environ.get("AITER_SITUV2_A4W4") == "1"
588
+ assert "AITER_SITUV2_A8W4" not in os.environ
589
+
590
+
591
+def test_aiter_moe_situv2_legacy_a8w4_alias_enables_a4w4(
592
+ monkeypatch: pytest.MonkeyPatch,
593
+):
594
+ """Deprecated VLLM_ROCM_USE_AITER_MOE_SITUV2_A8W4 still enables SiTUv2 a4w4."""
595
+ from vllm._aiter_ops import rocm_aiter_ops
596
+
597
+ _assert_aiter_supported()
598
+
599
+ with monkeypatch.context() as mp:
600
+ mp.delenv("AITER_SITUV2_A8W4", raising=False)
601
+ mp.delenv("AITER_SITUV2_A4W4", raising=False)
602
+ mp.delenv("VLLM_ROCM_USE_AITER_MOE_SITUV2", raising=False)
603
+ mp.setenv("VLLM_ROCM_USE_AITER", "1")
604
+ mp.setenv("VLLM_ROCM_USE_AITER_MOE", "1")
605
+ mp.setenv("VLLM_ROCM_USE_AITER_MOE_SITUV2_A8W4", "1")
606
+ _reload_envs()
607
+ rocm_aiter_ops.refresh_env_variables()
608
+
609
+ import os
610
+
611
+ assert os.environ.get("AITER_SITUV2_A4W4") == "1"
612
+ assert "AITER_SITUV2_A8W4" not in os.environ
613
+
614
+
615
+def test_aiter_moe_situv2_clears_aiter_env_when_disabled(
616
+ monkeypatch: pytest.MonkeyPatch,
617
+):
618
+ from vllm._aiter_ops import rocm_aiter_ops
619
+
620
+ _assert_aiter_supported()
621
+
622
+ with monkeypatch.context() as mp:
623
+ mp.setenv("AITER_SITUV2_A4W4", "1")
624
+ mp.setenv("VLLM_ROCM_USE_AITER", "1")
625
+ mp.setenv("VLLM_ROCM_USE_AITER_MOE", "1")
626
+ mp.delenv("VLLM_ROCM_USE_AITER_MOE_SITUV2_A8W4", raising=False)
627
+ mp.setenv("VLLM_ROCM_USE_AITER_MOE_SITUV2", "0")
628
+ _reload_envs()
629
+ rocm_aiter_ops.refresh_env_variables()
630
+
631
+ import os
632
+
633
+ assert "AITER_SITUV2_A4W4" not in os.environ
634
+
635
+
636
@pytest.mark.parametrize("moe_padding", [True, False])
637
def test_aiter_moe_padding_env_var(
638
moe_padding: bool,
vllm/_aiter_ops.py
+32
-6
@@ -1752,6 +1752,30 @@ def _mhc_delayed_pre_tail(
1752
_OPS_REGISTERED = False
1753
1754
1755
+def _sync_aiter_situv2_moe_env() -> None:
1756
+ """Mirror the SiTUv2 MoE toggle into AITER's a4w4 dispatch env.
1757
+
1758
+ AITER selects afp8 vs afp4 activation kernels via AITER_SITUV2_A8W4 /
1759
+ AITER_SITUV2_A4W4 (see ROCm/aiter fused_moe.py, A8W4 checked first).
1760
+ When VLLM_ROCM_USE_AITER_MOE_SITUV2 is enabled we route to a4w4
1761
+ (afp4_wfp4_fp4 kernels) and clear any legacy AITER_SITUV2_A8W4 override.
1762
+
1763
+ Requires AITER with ROCm/aiter#4463 (first tagged in v0.1.20): a4w4
1764
+ dispatch plus kimik3_a4w4_{un,}tuned_fmoe.csv. Older AITER still runs
1765
+ afp4 FlyDSL but falls back to heuristic configs, which is not the
1766
+ tuned a4w4 path this recipe is meant to use.
1767
+ """
1768
+ import os
1769
+
1770
+ import vllm.envs as envs
1771
+
1772
+ if envs.VLLM_ROCM_USE_AITER_MOE_SITUV2:
1773
+ os.environ["AITER_SITUV2_A4W4"] = "1"
1774
+ os.environ.pop("AITER_SITUV2_A8W4", None)
1775
+ else:
1776
+ os.environ.pop("AITER_SITUV2_A4W4", None)
1777
+
1778
+
1779
class rocm_aiter_ops:
1780
"""ROCm AITER operations wrapper for AMD GPU acceleration in vLLM.
1781
@@ -1774,7 +1798,7 @@ class rocm_aiter_ops:
1798
VLLM_ROCM_USE_AITER_FP8BMM: Controls FP8 batched matrix multiply.
1799
VLLM_ROCM_USE_AITER_TRITON_ROPE: Controls Triton rotary embeddings.
1800
VLLM_ROCM_USE_AITER_FUSION_SHARED_EXPERTS: Controls shared expert fusion.
1777
- VLLM_ROCM_USE_AITER_MOE_SITUV2_A8W4: Controls a8w4 SiTU fused MoE variant.
1801
+ VLLM_ROCM_USE_AITER_MOE_SITUV2: Controls SiTUv2 FlyDSL MoE (a4w4).
1802
VLLM_ROCM_USE_AITER_TRITON_GEMM: Controls Triton unquantized GEMM.
1803
1804
Note:
@@ -1841,7 +1865,7 @@ class rocm_aiter_ops:
1865
# TODO: Consolidate under VLLM_ROCM_USE_AITER_ROPE
1866
_TRITON_ROTARY_EMBED = envs.VLLM_ROCM_USE_AITER_TRITON_ROPE
1867
_MOE_SHARED_EXPERTS_ENABLED = envs.VLLM_ROCM_USE_AITER_FUSION_SHARED_EXPERTS
1844
- _MOE_SITUV2_A8W4 = envs.VLLM_ROCM_USE_AITER_MOE_SITUV2_A8W4
1868
+ _MOE_SITUV2 = envs.VLLM_ROCM_USE_AITER_MOE_SITUV2
1869
# TODO: Consolidate under _LINEAR_ENABLED
1870
_TRITON_UNQUANT_GEMM = envs.VLLM_ROCM_USE_AITER_TRITON_GEMM
1871
# Lazily probed: whether aiter.topk_softmax supports the
@@ -1870,7 +1894,8 @@ class rocm_aiter_ops:
1894
cls._LINEAR_HIPBMM_ENABLED = envs.VLLM_ROCM_USE_AITER_LINEAR_HIPBMM
1895
cls._TRITON_ROTARY_EMBED = envs.VLLM_ROCM_USE_AITER_TRITON_ROPE
1896
cls._MOE_SHARED_EXPERTS_ENABLED = envs.VLLM_ROCM_USE_AITER_FUSION_SHARED_EXPERTS
1873
- cls._MOE_SITUV2_A8W4 = envs.VLLM_ROCM_USE_AITER_MOE_SITUV2_A8W4
1897
+ cls._MOE_SITUV2 = envs.VLLM_ROCM_USE_AITER_MOE_SITUV2
1898
+ _sync_aiter_situv2_moe_env()
1899
cls._TRITON_UNQUANT_GEMM = envs.VLLM_ROCM_USE_AITER_TRITON_GEMM
1900
1901
@staticmethod
@@ -1983,10 +2008,10 @@ class rocm_aiter_ops:
2008
2009
@classmethod
2010
@if_aiter_supported
1986
- def is_fused_moe_situv2_a8w4_enabled(cls) -> bool:
1987
- # _MOE_SITUV2_A8W4 is a variant of aiter fused moe, so aiter
2011
+ def is_fused_moe_situv2_enabled(cls) -> bool:
2012
+ # _MOE_SITUV2 is a variant of aiter fused moe, so aiter
2013
# fused moe must be enabled as well.
1989
- return cls.is_fused_moe_enabled() and cls._MOE_SITUV2_A8W4
2014
+ return cls.is_fused_moe_enabled() and cls._MOE_SITUV2
2015
2016
@classmethod
2017
@if_aiter_supported
@@ -4029,3 +4054,4 @@ class rocm_aiter_ops:
4054
4055
4056
rocm_aiter_ops.register_ops_once()
4057
+_sync_aiter_situv2_moe_env()
vllm/envs.py
+13
-7
@@ -137,7 +137,7 @@ if TYPE_CHECKING:
137
VLLM_ROCM_USE_AITER_LINEAR_HIPBMM: bool = False
138
VLLM_ROCM_USE_AITER_MOE: bool = True
139
VLLM_ROCM_AITER_MOE_DISPATCH_POLICY: int = 0
140
- VLLM_ROCM_USE_AITER_MOE_SITUV2_A8W4: bool = False
140
+ VLLM_ROCM_USE_AITER_MOE_SITUV2: bool = False
141
VLLM_ROCM_USE_AITER_RMSNORM: bool = True
142
VLLM_ROCM_USE_AITER_MLA: bool = True
143
VLLM_ROCM_AITER_MLA_ASM_PADDING: Literal["auto", "gluon", "asm"] = "auto"
@@ -1251,12 +1251,18 @@ environment_variables: dict[str, Callable[[], Any]] = {
1251
"VLLM_ROCM_USE_AITER_MOE": lambda: (
1252
os.getenv("VLLM_ROCM_USE_AITER_MOE", "True").lower() in ("true", "1")
1253
),
1254
- # Route K3 SiTU MXFP4 MoE through the a8w4 (fp8 activation) gate/up-
1255
- # interleaved flydsl kernels instead of the default a16w4 separated path.
1256
- # This is the only flag users need: vLLM picks the kernels by passing
1257
- # gate_mode to AITER and sets the AITER-side workaround env at init.
1258
- "VLLM_ROCM_USE_AITER_MOE_SITUV2_A8W4": lambda: (
1259
- os.getenv("VLLM_ROCM_USE_AITER_MOE_SITUV2_A8W4", "0").lower() in ("true", "1")
1254
+ # Route K3 SiTU MXFP4 MoE through the FlyDSL SiTUv2 path (a4w4 fp4
1255
+ # activations, separated gate/up layout) instead of default a16w4. vLLM
1256
+ # sets AITER_SITUV2_A4W4 at init when this flag is on and clears any
1257
+ # legacy AITER_SITUV2_A8W4 override (AITER checks A8W4 first).
1258
+ # VLLM_ROCM_USE_AITER_MOE_SITUV2_A8W4 is a deprecated alias for existing
1259
+ # recipes; it does not select a8w4 kernels.
1260
+ # Needs AITER >= v0.1.20 (ROCm/aiter#4463) for the a4w4 dispatch flag
1261
+ # and tuned kimik3_a4w4_*_fmoe.csv rows; otherwise FlyDSL uses heuristics.
1262
+ "VLLM_ROCM_USE_AITER_MOE_SITUV2": lambda: (
1263
+ os.getenv("VLLM_ROCM_USE_AITER_MOE_SITUV2", "0").lower() in ("true", "1")
1264
+ or os.getenv("VLLM_ROCM_USE_AITER_MOE_SITUV2_A8W4", "0").lower()
1265
+ in ("true", "1")
1266
),
1267
# MoE sorting dispatch policy for AITER fused MoE kernels.
1268
# 0 = auto (default): single-pass for small batches, multi-pass
vllm/model_executor/layers/fused_moe/experts/rocm_aiter_moe.py
+4
-8
@@ -385,14 +385,10 @@ def rocm_aiter_fused_experts(
385
386
gate_mode = ""
387
if activation == MoEActivation.SITU:
388
- # a8w4 (VLLM_ROCM_USE_AITER_MOE_SITUV2_A8W4=1) uses the gate/up-
389
- # interleaved (_gui_) fp8 flydsl kernels; default a16w4 SiTU stays
390
- # separated.
391
- gate_mode = (
392
- GateMode.INTERLEAVE.value
393
- if rocm_aiter_ops.is_fused_moe_situv2_a8w4_enabled()
394
- else GateMode.SEPARATED.value
395
- )
388
+ # SiTUv2 flydsl (VLLM_ROCM_USE_AITER_MOE_SITUV2=1) uses a4w4
389
+ # fp4 activations with separated gate/up weights (AITER #4463);
390
+ # default a16w4 SiTU also stays separated.
391
+ gate_mode = GateMode.SEPARATED.value
392
elif quant_config.use_mxfp4_w4a16:
393
gate_mode = GateMode.INTERLEAVE.value
394
elif activation_interleave is not None:
vllm/model_executor/layers/fused_moe/oracle/mxfp4.py
+10
-9
@@ -1572,12 +1572,6 @@ def convert_weight_to_mxfp4_moe_kernel_format(
1572
if w2_bias is not None:
1573
w2_bias = w2_bias.data.to(torch.float32)
1574
1575
- import os
1576
-
1577
- # TODO: Remove this once AITER is fixed
1578
- # Necessary for AITER side from crashing
1579
- os.environ["AITER_BF16_FP8_MOE_BOUND"] = "0"
1580
-
1575
if activation == MoEActivation.SITU:
1576
from aiter.utility.fp4_utils import e8m0_shuffle
1577
@@ -1585,9 +1579,8 @@ def convert_weight_to_mxfp4_moe_kernel_format(
1579
1580
fp4_dtype = torch.float4_e2m1fn_x2
1581
e8m0_dtype = torch.float8_e8m0fnu
1588
- # a8w4 uses gate/up-interleaved flydsl kernels;
1589
- # default a16w4 keeps the separated layout.
1590
- guinterleave = rocm_aiter_ops.is_fused_moe_situv2_a8w4_enabled()
1582
+ # SiTUv2 flydsl uses separated gate/up layout (a4w4).
1583
+ guinterleave = False
1584
w13 = rocm_aiter_ops.shuffle_weight_a16w4(
1585
w13_weight.data.view(fp4_dtype), 16, guinterleave
1586
)
@@ -1606,6 +1599,14 @@ def convert_weight_to_mxfp4_moe_kernel_format(
1599
w2.is_shuffled = True
1600
return (w13, w2, w13_scale, w2_scale, w13_bias, w2_bias)
1601
1602
+ import os
1603
+
1604
+ # Interleaved a16w4 only (DeepSeekV4 etc.). AITER uses this bound to
1605
+ # pick bf16 vs fp8 activations when gate_mode is INTERLEAVE. SiTUv2
1606
+ # a4w4 is separated and selects q_dtype_a independently, so the bound
1607
+ # is unused on that path.
1608
+ os.environ["AITER_BF16_FP8_MOE_BOUND"] = "0"
1609
+
1610
from aiter.ops.shuffle import shuffle_scale as _shuf_s
1611
from aiter.ops.shuffle import shuffle_weight as _shuf_w
1612