A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 4, 22:30 UTC
[ROCm] Add MLA dual RMS norm fusion (Q, KV) pass for DeepSeek/Kimi-K2 (#39242)
Signed-off-by: Rita Brugarolas Brufau <rita.brugarolasbrufau@amd.com>
Rita Brugarolas committed
Apr 20, 2026 at 07:56 UTC
fb5635d3f90635ce9d1acbc975baab6e911a262b
8 files changed
+361
-1
docs/design/fusions.md
+40
@@ -31,6 +31,7 @@ or just on the low or high end.
31
| [RMSNorm + Quant](#rmsnorm--quantization-fuse_norm_quant) | `fuse_norm_quant` | RMSNorm (+residual add) → FP8/FP4 quant | O1 (conditional) | 1-4% | No | Always |
32
| [SiLU+Mul + Quant](#silumul--quantization-fuse_act_quant) | `fuse_act_quant` | SiLU+Mul activation → FP8/FP4 quant | O1 (conditional) | 1-4% | No | Always |
33
| [RMSNorm + Padding](#rmsnorm--padding-fuse_act_padding) | `fuse_act_padding` | Residual add + RMSNorm → padding | O1 (ROCm/AITER only) | TBD | No | Always |
34
+| [MLA Dual RMSNorm](#mla-dual-rmsnorm-fuse_mla_dual_rms_norm) | `fuse_mla_dual_rms_norm` | Paired Q + KV RMSNorm → single kernel | O1 (ROCm/AITER only) | ~2% | No | Always |
35
36
## Support Matrix
37
@@ -51,6 +52,7 @@ The table below lists the quantization schemes supported by each fusion on each
52
| `fuse_norm_quant` | FP8 static, FP8 per-token, FP8 per-group | FP8 static, FP8 per-token, FP8 per-group | FP8 static, FP8 per-token, FP8 per-group | — | FP8 static, FP8 per-token, FP8 per-group |
53
| `fuse_act_quant` | FP8 static, NVFP4 | FP8 static, FP8 per-group (128/64) | FP8 static, FP8 per-group (128/64) | — | FP8 per-group |
54
| `fuse_act_padding` | — | — | — | — | FP16/BF16 |
55
+| `fuse_mla_dual_rms_norm` | — | — | — | — | BF16 |
56
57
\* `fuse_attn_quant` support depends on the attention backend in use; not all backends support
58
fused quantization output. See the [`fuse_attn_quant` section](#attention--quantization-fuse_attn_quant)
@@ -381,6 +383,44 @@ when the hidden size is 2880 and AITER Triton GEMMs *not* enabled.
383
384
- Pass: [`vllm/compilation/passes/fusion/rocm_aiter_fusion.py`](https://github.com/vllm-project/vllm/blob/main/vllm/compilation/passes/fusion/rocm_aiter_fusion.py) (`RocmAiterTritonAddRMSNormPadFusionPass`)
385
386
+### MLA Dual RMSNorm (`fuse_mla_dual_rms_norm`)
387
+
388
+!!! info
389
+ ROCm/AITER-only. Targeted at DeepSeek-V3 / Kimi-K2 MLA attention.
390
+
391
+!!! note
392
+ When the native implementation of `rms_norm` is used (the default on CUDA and
393
+ ROCm for now), Inductor's built-in fusion already handles merging these norms
394
+ automatically. This explicit pass targets the case where AITER's custom
395
+ `rms_norm` op is active, which Inductor cannot fuse on its own.
396
+
397
+**What it fuses.** Fuses the paired `q_a_layernorm` and `kv_a_layernorm` RMS norm
398
+operations in MLA attention into a single `fused_qk_rmsnorm` HIP kernel call via AITER,
399
+reducing kernel launch overhead from 2 launches to 1 per MLA layer.
400
+
401
+```text
402
+# Unfused:
403
+q_c, kv_lora = split(projected, [q_dim, kv_dim])
404
+kv_c, k_pe = split(kv_lora, [kv_c_dim, k_pe_dim])
405
+q_c = rms_norm(q_c, q_weight, eps)
406
+kv_c = rms_norm(kv_c, kv_weight, eps)
407
+
408
+# Fused:
409
+q_c, kv_lora = split(projected, [q_dim, kv_dim])
410
+kv_c, k_pe = split(kv_lora, [kv_c_dim, k_pe_dim])
411
+q_normed, kv_normed = fused_mla_dual_rms_norm(
412
+ q_c, q_weight, kv_c, kv_weight, eps1, eps2)
413
+```
414
+
415
+Requires: AMD ROCm with AITER enabled. Enabled by default at optimization level O1 and above
416
+when AITER is available.
417
+
418
+**Code locations.**
419
+
420
+- Pass: [`vllm/compilation/passes/fusion/rocm_aiter_fusion.py`](https://github.com/vllm-project/vllm/blob/main/vllm/compilation/passes/fusion/rocm_aiter_fusion.py) (`MLADualRMSNormFusionPass`)
421
+- Custom op: [`vllm/_aiter_ops.py`](https://github.com/vllm-project/vllm/blob/main/vllm/_aiter_ops.py) (`fused_mla_dual_rms_norm`)
422
+- AITER kernel: [`fused_qk_rmsnorm`](https://github.com/ROCm/aiter/pull/2442)
423
+
424
## See Also
425
426
- [Optimization Levels](optimization_levels.md) — high-level presets that set
docs/design/optimization_levels.md
+1
@@ -56,6 +56,7 @@ Fusions:
56
- `-cc.pass_config.fuse_norm_quant=True`*
57
- `-cc.pass_config.fuse_act_quant=True`*
58
- `-cc.pass_config.fuse_act_padding=True`†
59
+- `-cc.pass_config.fuse_mla_dual_rms_norm=True`†
60
61
\* These fusions are only enabled when either op is using a custom kernel, otherwise Inductor fusion is better.</br>
62
† These fusions are ROCm-only and require AITER.
tests/compile/passes/test_fuse_mla_dual_rms_norm.py
new
+148
@@ -0,0 +1,148 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+"""
4
+Unit test for the MLADualRMSNormFusionPass.
5
+
6
+The pass fuses paired q/kv RMS norms in MLA attention into a single
7
+fused_mla_dual_rms_norm op backed by AITER's fused_qk_rmsnorm kernel.
8
+"""
9
+
10
+import pytest
11
+import torch
12
+
13
+import vllm.config
14
+from tests.compile.backend import TestBackend
15
+from vllm._aiter_ops import is_aiter_found_and_supported, rocm_aiter_ops
16
+from vllm.compilation.passes.utility.noop_elimination import NoOpEliminationPass
17
+from vllm.compilation.passes.utility.post_cleanup import PostCleanupPass
18
+from vllm.config import (
19
+ CompilationConfig,
20
+ CompilationMode,
21
+ ModelConfig,
22
+ PassConfig,
23
+ VllmConfig,
24
+)
25
+from vllm.model_executor.layers.layernorm import RMSNorm
26
+
27
+# MLA attention geometry for DeepSeek-V3 / Kimi-K2
28
+Q_DIM = 1536
29
+KV_C_DIM = 512
30
+K_PE_DIM = 64
31
+EPS = 1e-6
32
+
33
+
34
+class MLADualRMSNormTestModel(torch.nn.Module):
35
+ """
36
+ Minimal model reproducing the MLA dual RMS norm pattern:
37
+ linear -> split([q_dim, kv_dim])
38
+ +-- q_c (getitem 0) -> rms_norm(q_w, eps) -> linear
39
+ +-- kv_lora (getitem 1) -> split([kv_c_dim, k_pe_dim])
40
+ +-- kv_c (getitem 0) -> rms_norm(kv_w, eps)
41
+ +-- k_pe
42
+ """
43
+
44
+ def __init__(
45
+ self,
46
+ hidden_size: int,
47
+ q_dim: int = Q_DIM,
48
+ kv_c_dim: int = KV_C_DIM,
49
+ k_pe_dim: int = K_PE_DIM,
50
+ eps: float = EPS,
51
+ ):
52
+ super().__init__()
53
+ self.q_dim = q_dim
54
+ self.kv_dim = kv_c_dim + k_pe_dim
55
+ self.kv_c_dim = kv_c_dim
56
+ self.k_pe_dim = k_pe_dim
57
+
58
+ self.proj = torch.nn.Linear(hidden_size, q_dim + self.kv_dim, bias=False)
59
+ self.q_norm = RMSNorm(q_dim, eps=eps)
60
+ self.kv_norm = RMSNorm(kv_c_dim, eps=eps)
61
+ self.q_b_proj = torch.nn.Linear(q_dim, hidden_size, bias=False)
62
+
63
+ def forward(
64
+ self, x: torch.Tensor
65
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
66
+ # Avoid graph input being a direct arg to a matched pattern node
67
+ x = torch.relu(x)
68
+
69
+ projected = self.proj(x)
70
+
71
+ q_c, kv_lora = projected.split([self.q_dim, self.kv_dim], dim=-1)
72
+ kv_c, k_pe = kv_lora.split([self.kv_c_dim, self.k_pe_dim], dim=-1)
73
+
74
+ q_normed = self.q_norm(q_c)
75
+ kv_normed = self.kv_norm(kv_c)
76
+
77
+ q_out = self.q_b_proj(q_normed)
78
+ return q_out, kv_normed, k_pe
79
+
80
+ def ops_in_model_before(self):
81
+ return [torch.ops.vllm_ir.rms_norm.default]
82
+
83
+ def ops_in_model_after(self):
84
+ return [torch.ops.vllm.fused_mla_dual_rms_norm.default]
85
+
86
+
87
+@pytest.mark.parametrize("dtype", [torch.bfloat16])
88
+@pytest.mark.parametrize("hidden_size", [7168])
89
+@pytest.mark.skipif(
90
+ not is_aiter_found_and_supported(),
91
+ reason="Only test on ROCm with AITER installed and supported",
92
+)
93
+def test_fuse_mla_dual_rms_norm(
94
+ dtype: torch.dtype,
95
+ hidden_size: int,
96
+ monkeypatch: pytest.MonkeyPatch,
97
+):
98
+ torch._dynamo.reset()
99
+
100
+ vllm_config = VllmConfig(
101
+ model_config=ModelConfig(dtype=dtype),
102
+ compilation_config=CompilationConfig(
103
+ mode=CompilationMode.VLLM_COMPILE,
104
+ custom_ops=["+rms_norm"],
105
+ pass_config=PassConfig(
106
+ fuse_mla_dual_rms_norm=True,
107
+ eliminate_noops=True,
108
+ ),
109
+ ),
110
+ )
111
+
112
+ with vllm.config.set_current_vllm_config(vllm_config), monkeypatch.context() as m:
113
+ from vllm.compilation.passes.fusion.rocm_aiter_fusion import (
114
+ MLADualRMSNormFusionPass,
115
+ )
116
+
117
+ torch.set_default_device("cuda")
118
+ torch.set_default_dtype(dtype)
119
+ torch.manual_seed(42)
120
+
121
+ m.setenv("VLLM_ROCM_USE_AITER", "1")
122
+ rocm_aiter_ops.refresh_env_variables()
123
+
124
+ fusion_pass = MLADualRMSNormFusionPass(vllm_config)
125
+ passes = [
126
+ NoOpEliminationPass(vllm_config),
127
+ fusion_pass,
128
+ PostCleanupPass(vllm_config),
129
+ ]
130
+ backend = TestBackend(*passes)
131
+ model = MLADualRMSNormTestModel(hidden_size)
132
+
133
+ x = torch.randn(1, hidden_size)
134
+ torch._dynamo.mark_dynamic(x, 0)
135
+
136
+ outputs_unfused = model(x)
137
+
138
+ model_fused = torch.compile(model, backend=backend)
139
+ outputs_fused = model_fused(x)
140
+
141
+ torch.testing.assert_close(outputs_unfused, outputs_fused, atol=1e-2, rtol=1e-2)
142
+
143
+ assert fusion_pass.matched_count == 1, (
144
+ f"Expected 1 fused pair, got {fusion_pass.matched_count}"
145
+ )
146
+
147
+ backend.check_before_ops(model.ops_in_model_before())
148
+ backend.check_after_ops(model.ops_in_model_after())
vllm/_aiter_ops.py
+42
@@ -962,6 +962,37 @@ def _rocm_aiter_triton_add_rmsnorm_pad_fake(
962
return out, residual_out
963
964
965
+def _fused_mla_dual_rms_norm_impl(
966
+ x1: torch.Tensor,
967
+ x1_weight: torch.Tensor,
968
+ x2: torch.Tensor,
969
+ x2_weight: torch.Tensor,
970
+ x1_epsilon: float,
971
+ x2_epsilon: float,
972
+) -> tuple[torch.Tensor, torch.Tensor]:
973
+ from aiter.ops.fused_qk_norm_rope_cache_quant import fused_qk_rmsnorm
974
+
975
+ return fused_qk_rmsnorm(
976
+ q=x1,
977
+ q_weight=x1_weight,
978
+ q_eps=x1_epsilon,
979
+ k=x2,
980
+ k_weight=x2_weight,
981
+ k_eps=x2_epsilon,
982
+ )
983
+
984
+
985
+def _fused_mla_dual_rms_norm_fake(
986
+ x1: torch.Tensor,
987
+ x1_weight: torch.Tensor,
988
+ x2: torch.Tensor,
989
+ x2_weight: torch.Tensor,
990
+ x1_epsilon: float,
991
+ x2_epsilon: float,
992
+) -> tuple[torch.Tensor, torch.Tensor]:
993
+ return (torch.empty_like(x1), torch.empty_like(x2))
994
+
995
+
996
def _rocm_aiter_gemm_a8wfp4_impl(
997
x: torch.Tensor,
998
w: torch.Tensor,
@@ -1491,6 +1522,13 @@ class rocm_aiter_ops:
1522
fake_impl=_triton_rotary_embedding_fake,
1523
)
1524
1525
+ direct_register_custom_op(
1526
+ op_name="fused_mla_dual_rms_norm",
1527
+ op_func=_fused_mla_dual_rms_norm_impl,
1528
+ mutates_args=[],
1529
+ fake_impl=_fused_mla_dual_rms_norm_fake,
1530
+ )
1531
+
1532
_OPS_REGISTERED = True
1533
1534
@staticmethod
@@ -1537,6 +1575,10 @@ class rocm_aiter_ops:
1575
def get_triton_rotary_embedding_op() -> OpOverload:
1576
return torch.ops.vllm.rocm_aiter_triton_rotary_embedding.default
1577
1578
+ @staticmethod
1579
+ def get_fused_mla_dual_rms_norm_op() -> OpOverload:
1580
+ return torch.ops.vllm.fused_mla_dual_rms_norm.default
1581
+
1582
@staticmethod
1583
def rms_norm(
1584
x: torch.Tensor, weight: torch.Tensor, variance_epsilon: float
vllm/compilation/passes/fusion/rocm_aiter_fusion.py
+106
-1
@@ -1,5 +1,6 @@
1
# SPDX-License-Identifier: Apache-2.0
2
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+from collections.abc import Callable
4
from typing import Any
5
6
import torch
@@ -7,6 +8,7 @@ import torch._inductor.pattern_matcher as pm
8
from torch import fx
9
from torch._inductor.pattern_matcher import PatternMatcherPass
10
11
+import vllm.ir.ops
12
import vllm.model_executor.layers.quantization.utils.fp8_utils # noqa: F401
13
from vllm._aiter_ops import rocm_aiter_ops
14
from vllm.config import VllmConfig
@@ -20,7 +22,12 @@ from vllm.model_executor.layers.quantization.utils.quant_utils import (
22
from vllm.platforms import current_platform
23
24
from ..inductor_pass import enable_fake_mode
23
-from ..vllm_inductor_pass import VllmInductorPass, VllmPatternMatcherPass
25
+from ..vllm_inductor_pass import (
26
+ VllmFusionPatternMatcherPass,
27
+ VllmInductorPass,
28
+ VllmPatternMatcherPass,
29
+ VllmPatternReplacement,
30
+)
31
from .act_quant_fusion import ActivationQuantPattern
32
from .matcher_utils import (
33
MatcherFusedAddRMSNorm,
@@ -512,3 +519,101 @@ class RocmAiterTritonAddRMSNormPadFusionPass(VllmPatternMatcherPass):
519
520
def uuid(self) -> str:
521
return VllmInductorPass.hash_source(self, AddAiterRMSNormPadPattern)
522
+
523
+
524
+class MLADualRMSNormPattern(
525
+ VllmPatternReplacement[..., tuple[torch.Tensor, torch.Tensor, torch.Tensor]]
526
+):
527
+ """
528
+ Fuse paired q_a_layernorm + kv_a_layernorm in MLA attention into
529
+ AITER's ``fused_qk_rmsnorm`` HIP kernel.
530
+
531
+ Target FX-graph pattern (unfused, ``vllm_ir`` stage)::
532
+
533
+ gemm -> split_with_sizes([q_dim, kv_dim])
534
+ +-- q_c -> vllm_ir.rms_norm(q_c, q_w, eps)
535
+ +-- kv_lora -> split_with_sizes([kv_c_dim, k_pe_dim])
536
+ +-- kv_c -> vllm_ir.rms_norm(kv_c, kv_w, eps)
537
+ +-- k_pe
538
+
539
+ The pattern covers the connected subgraph rooted at the first
540
+ ``split_with_sizes`` (which produces ``q_c`` and ``kv_lora``),
541
+ through the two ``rms_norm`` calls, and the ``k_pe`` passthrough.
542
+ """
543
+
544
+ def __init__(self, epsilon: float) -> None:
545
+ self._epsilon = epsilon
546
+
547
+ def get_inputs(self) -> list[torch.Tensor]:
548
+ q_dim, kv_c_dim, k_pe_dim = 8, 4, 2
549
+ return [
550
+ self.empty_bf16(5, q_dim + kv_c_dim + k_pe_dim),
551
+ self.empty_bf16(q_dim),
552
+ self.empty_bf16(kv_c_dim),
553
+ ]
554
+
555
+ @property
556
+ def pattern(
557
+ self,
558
+ ) -> Callable[..., tuple[torch.Tensor, torch.Tensor, torch.Tensor]]:
559
+ eps = self._epsilon
560
+
561
+ def _pattern(
562
+ projected: torch.Tensor,
563
+ q_weight: torch.Tensor,
564
+ kv_weight: torch.Tensor,
565
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
566
+ q_dim = q_weight.shape[0]
567
+ kv_dim = projected.shape[-1] - q_dim
568
+ kv_c_dim = kv_weight.shape[0]
569
+ k_pe_dim = kv_dim - kv_c_dim
570
+ q_c, kv_lora = projected.split([q_dim, kv_dim], dim=-1)
571
+ kv_c, k_pe = kv_lora.split([kv_c_dim, k_pe_dim], dim=-1)
572
+ q_normed = vllm.ir.ops.rms_norm(q_c, q_weight, eps)
573
+ kv_normed = vllm.ir.ops.rms_norm(kv_c, kv_weight, eps)
574
+ return q_normed, kv_normed, k_pe
575
+
576
+ return _pattern
577
+
578
+ @property
579
+ def replacement(
580
+ self,
581
+ ) -> Callable[..., tuple[torch.Tensor, torch.Tensor, torch.Tensor]]:
582
+ eps = self._epsilon
583
+
584
+ def _replacement(
585
+ projected: torch.Tensor,
586
+ q_weight: torch.Tensor,
587
+ kv_weight: torch.Tensor,
588
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
589
+ q_dim = q_weight.shape[0]
590
+ kv_dim = projected.shape[-1] - q_dim
591
+ kv_c_dim = kv_weight.shape[0]
592
+ k_pe_dim = kv_dim - kv_c_dim
593
+ q_c, kv_lora = projected.split([q_dim, kv_dim], dim=-1)
594
+ kv_c, k_pe = kv_lora.split([kv_c_dim, k_pe_dim], dim=-1)
595
+ q_normed, kv_normed = torch.ops.vllm.fused_mla_dual_rms_norm(
596
+ q_c,
597
+ q_weight,
598
+ kv_c,
599
+ kv_weight,
600
+ eps,
601
+ eps,
602
+ )
603
+ return q_normed, kv_normed, k_pe
604
+
605
+ return _replacement
606
+
607
+
608
+class MLADualRMSNormFusionPass(VllmFusionPatternMatcherPass):
609
+ """
610
+ Post-grad PatternMatcher pass that fuses paired q / kv RMS norms in
611
+ MLA attention into ``fused_mla_dual_rms_norm`` backed by aiter's
612
+ ``fused_qk_rmsnorm`` HIP kernel.
613
+ """
614
+
615
+ def __init__(self, config: VllmConfig) -> None:
616
+ super().__init__(config, "mla_dual_rms_norm_fusion_pass")
617
+
618
+ for epsilon in [1e-5, 1e-6]:
619
+ self.register(MLADualRMSNormPattern(epsilon))
vllm/compilation/passes/pass_manager.py
+4
@@ -19,6 +19,7 @@ from .vllm_inductor_pass import VllmInductorPass, VllmPatternMatcherPass
19
20
if rocm_aiter_ops.is_enabled():
21
from .fusion.rocm_aiter_fusion import (
22
+ MLADualRMSNormFusionPass,
23
RocmAiterRMSNormQuantFusionPass,
24
RocmAiterSiluMulFp8GroupQuantFusionPass,
25
RocmAiterTritonAddRMSNormPadFusionPass,
@@ -155,6 +156,9 @@ class PostGradPassManager(CustomGraphPass): # type: ignore[misc]
156
if self.pass_config.fuse_act_padding and rocm_aiter_ops.is_enabled():
157
self.passes += [RocmAiterTritonAddRMSNormPadFusionPass(config)]
158
159
+ if self.pass_config.fuse_mla_dual_rms_norm and rocm_aiter_ops.is_enabled():
160
+ self.passes += [MLADualRMSNormFusionPass(config)]
161
+
162
if self.pass_config.fuse_rope_kvcache:
163
self.passes += [SplitCoalescingPass(config)]
164
self.passes += [ScatterSplitReplacementPass(config)]
vllm/config/compilation.py
+9
@@ -142,6 +142,8 @@ class PassConfig:
142
# ROCm/AITER specific fusions
143
fuse_act_padding: bool = None # type: ignore[assignment]
144
"""Fuse the custom RMSNorm + padding ops."""
145
+ fuse_mla_dual_rms_norm: bool = None # type: ignore[assignment]
146
+ """Fuse paired q/kv RMS norms in MLA attention."""
147
fuse_rope_kvcache: bool = None # type: ignore[assignment]
148
"""Fuse the QK rope + KV cache ops."""
149
@@ -224,6 +226,7 @@ class PassConfig:
226
"fuse_gemm_comms",
227
"fuse_allreduce_rms",
228
"fuse_act_padding",
229
+ "fuse_mla_dual_rms_norm",
230
"fuse_rope_kvcache",
231
mode="wrap",
232
)
@@ -270,6 +273,12 @@ class PassConfig:
273
"The fusion will be disabled."
274
)
275
self.fuse_act_padding = False
276
+ if self.fuse_mla_dual_rms_norm and not current_platform.is_rocm():
277
+ logger.warning_once(
278
+ "MLA dual RMS norm fusion requires ROCm/AITER. "
279
+ "The fusion will be disabled."
280
+ )
281
+ self.fuse_mla_dual_rms_norm = False
282
if self.fuse_rope_kvcache and not current_platform.is_rocm():
283
logger.warning_once(
284
"KV cache fusion currently only enabled on ROCm. "
vllm/config/vllm.py
+11
@@ -165,6 +165,13 @@ def enable_norm_pad_fusion(cfg: "VllmConfig") -> bool:
165
)
166
167
168
+def enable_mla_dual_rms_norm_fusion(cfg: "VllmConfig") -> bool:
169
+ """Enable MLA dual RMS norm fusion when AITer is available."""
170
+ from vllm._aiter_ops import rocm_aiter_ops
171
+
172
+ return rocm_aiter_ops.is_enabled()
173
+
174
+
175
OPTIMIZATION_LEVEL_00 = {
176
"compilation_config": {
177
"pass_config": {
@@ -175,6 +182,7 @@ OPTIMIZATION_LEVEL_00 = {
182
"enable_sp": False,
183
"fuse_gemm_comms": False,
184
"fuse_act_padding": False,
185
+ "fuse_mla_dual_rms_norm": False,
186
"fuse_rope_kvcache": False,
187
},
188
"cudagraph_mode": CUDAGraphMode.NONE,
@@ -194,6 +202,7 @@ OPTIMIZATION_LEVEL_01 = {
202
"enable_sp": False,
203
"fuse_gemm_comms": False,
204
"fuse_act_padding": enable_norm_pad_fusion,
205
+ "fuse_mla_dual_rms_norm": enable_mla_dual_rms_norm_fusion,
206
"fuse_rope_kvcache": False,
207
},
208
"cudagraph_mode": CUDAGraphMode.PIECEWISE,
@@ -213,6 +222,7 @@ OPTIMIZATION_LEVEL_02 = {
222
"enable_sp": IS_DENSE,
223
"fuse_gemm_comms": IS_DENSE,
224
"fuse_act_padding": enable_norm_pad_fusion,
225
+ "fuse_mla_dual_rms_norm": enable_mla_dual_rms_norm_fusion,
226
"fuse_rope_kvcache": enable_rope_kvcache_fusion,
227
},
228
"cudagraph_mode": CUDAGraphMode.FULL_AND_PIECEWISE,
@@ -232,6 +242,7 @@ OPTIMIZATION_LEVEL_03 = {
242
"enable_sp": IS_DENSE,
243
"fuse_gemm_comms": IS_DENSE,
244
"fuse_act_padding": enable_norm_pad_fusion,
245
+ "fuse_mla_dual_rms_norm": enable_mla_dual_rms_norm_fusion,
246
"fuse_rope_kvcache": enable_rope_kvcache_fusion,
247
},
248
"cudagraph_mode": CUDAGraphMode.FULL_AND_PIECEWISE,