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 9, 21:48 UTC
[MoE Refactor] Oracle Select FP8+NVFP4 Kernels In Priority (#32414)
Robert Shaw committed
Jan 21, 2026 at 08:22 UTC
42135d689830c0e764d925b6454bc68ba6c6cab4
82 files changed
+2699
-1552
.buildkite/test-pipeline.yaml
+40
@@ -634,6 +634,46 @@ steps:
634
- pip install helion
635
- pytest -v -s kernels/helion/
636
637
+
638
+- label: Kernels FP8 MoE Test (1 H100)
639
+ timeout_in_minutes: 90
640
+ gpu: h100
641
+ num_gpus: 1
642
+ optional: true
643
+ commands:
644
+ - pytest -v -s kernels/moe/test_cutlass_moe.py
645
+ - pytest -v -s kernels/moe/test_flashinfer.py
646
+ - pytest -v -s kernels/moe/test_gpt_oss_triton_kernels.py
647
+ - pytest -v -s kernels/moe/test_modular_oai_triton_moe.py
648
+ - pytest -v -s kernels/moe/test_moe.py
649
+ # - pytest -v -s kernels/moe/test_block_fp8.py - failing on main
650
+ - pytest -v -s kernels/moe/test_block_int8.py
651
+ - pytest -v -s kernels/moe/test_triton_moe_no_act_mul.py
652
+ - pytest -v -s kernels/moe/test_triton_moe_ptpc_fp8.py
653
+
654
+- label: Kernels FP8 MoE Test (2 H100s)
655
+ timeout_in_minutes: 90
656
+ gpu: h100
657
+ num_gpus: 2
658
+ optional: true
659
+ commands:
660
+ - pytest -v -s kernels/moe/test_deepep_deepgemm_moe.py
661
+ - pytest -v -s kernels/moe/test_deepep_moe.py
662
+ - pytest -v -s kernels/moe/test_pplx_cutlass_moe.py
663
+ # - pytest -v -s kernels/moe/test_pplx_moe.py - failing on main
664
+
665
+- label: Kernels Fp4 MoE Test (B200)
666
+ timeout_in_minutes: 60
667
+ gpu: b200
668
+ num_gpus: 1
669
+ optional: true
670
+ commands:
671
+ - pytest -v -s kernels/moe/test_cutedsl_moe.py
672
+ - pytest -v -s kernels/moe/test_flashinfer_moe.py
673
+ - pytest -v -s kernels/moe/test_nvfp4_moe.py
674
+ - pytest -v -s kernels/moe/test_ocp_mx_moe.py
675
+
676
+
677
- label: Model Executor Test # 23min
678
timeout_in_minutes: 35
679
torch_nightly: true
benchmarks/kernels/benchmark_cutlass_moe_fp8.py
+7
-5
@@ -9,6 +9,7 @@ but use different quantization strategies and backends.
9
import torch
10
11
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
12
+from tests.kernels.moe.utils import make_dummy_moe_config
13
from vllm import _custom_ops as ops
14
from vllm.model_executor.layers.fused_moe.config import fp8_w8a8_moe_quant_config
15
from vllm.model_executor.layers.fused_moe.cutlass_moe import CutlassExpertsFp8
@@ -138,12 +139,13 @@ def bench_run(
139
fn = mk.FusedMoEModularKernel(
140
MoEPrepareAndFinalizeNoEP(),
141
CutlassExpertsFp8(
141
- out_dtype=a.dtype,
142
- e=num_experts,
143
- n=n,
144
- k=k,
142
+ moe_config=make_dummy_moe_config(
143
+ num_experts=num_experts,
144
+ hidden_dim=k,
145
+ intermediate_size_per_partition=n,
146
+ in_dtype=a.dtype,
147
+ ),
148
quant_config=quant_config,
146
- device=w1.device,
149
),
150
)
151
benchmarks/kernels/benchmark_cutlass_moe_nvfp4.py
+3
-4
@@ -12,6 +12,7 @@ import torch
12
import torch.utils.benchmark as benchmark
13
14
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
15
+from tests.kernels.moe.utils import make_dummy_moe_config
16
from vllm import _custom_ops as ops
17
from vllm.config import ParallelConfig, VllmConfig, set_current_vllm_config
18
from vllm.model_executor.layers.fused_moe.config import (
@@ -198,8 +199,7 @@ def bench_run(
199
kernel = mk.FusedMoEModularKernel(
200
MoEPrepareAndFinalizeNoEP(defer_input_quant=True),
201
CutlassExpertsFp4(
201
- out_dtype=dtype,
202
- max_experts_per_worker=e,
202
+ make_dummy_moe_config(),
203
quant_config=quant_config,
204
),
205
)
@@ -244,8 +244,7 @@ def bench_run(
244
kernel = mk.FusedMoEModularKernel(
245
MoEPrepareAndFinalizeNoEP(defer_input_quant=True),
246
CutlassExpertsFp4(
247
- out_dtype=dtype,
248
- max_experts_per_worker=e,
247
+ make_dummy_moe_config(),
248
quant_config=quant_config,
249
),
250
)
benchmarks/kernels/benchmark_grouped_gemm_cutlass.py
+13
-12
@@ -6,6 +6,7 @@ import torch.utils.benchmark as benchmark
6
from benchmark_shapes import WEIGHT_SHAPES_MOE
7
8
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
9
+from tests.kernels.moe.utils import make_dummy_moe_config
10
from vllm import _custom_ops as ops
11
from vllm.config import ParallelConfig, VllmConfig, set_current_vllm_config
12
from vllm.model_executor.layers.fused_moe.config import fp8_w8a8_moe_quant_config
@@ -134,13 +135,13 @@ def bench_run(
135
fn = mk.FusedMoEModularKernel(
136
MoEPrepareAndFinalizeNoEP(),
137
CutlassExpertsFp8(
137
- out_dtype=a.dtype,
138
- # NOTE(rob): w2 is shaped as [E, hidden, intermediate]
139
- e=w2.shape[0],
140
- n=w2.shape[2],
141
- k=w2.shape[1],
138
+ moe_config=make_dummy_moe_config(
139
+ num_experts=w2.shape[0],
140
+ hidden_dim=w2.shape[1],
141
+ intermediate_size_per_partition=w2.shape[2],
142
+ in_dtype=a.dtype,
143
+ ),
144
quant_config=quant_config,
143
- device=w1.device,
145
),
146
)
147
@@ -166,13 +167,13 @@ def bench_run(
167
fn = mk.FusedMoEModularKernel(
168
MoEPrepareAndFinalizeNoEP(),
169
CutlassExpertsFp8(
169
- out_dtype=a.dtype,
170
- # NOTE(rob): w2 is shaped as [E, hidden, intermediate]
171
- e=w2.shape[0],
172
- n=w2.shape[2],
173
- k=w2.shape[1],
170
+ moe_config=make_dummy_moe_config(
171
+ num_experts=w2.shape[0],
172
+ hidden_dim=w2.shape[1],
173
+ intermediate_size_per_partition=w2.shape[2],
174
+ in_dtype=a.dtype,
175
+ ),
176
quant_config=quant_config,
175
- device=w1.device,
177
),
178
)
179
benchmarks/kernels/benchmark_moe.py
+29
-1
@@ -16,10 +16,16 @@ import torch
16
from ray.experimental.tqdm_ray import tqdm
17
18
from vllm.model_executor.layers.fused_moe.config import (
19
+ FusedMoEConfig,
20
+ FusedMoEParallelConfig,
21
FusedMoEQuantConfig,
22
+ RoutingMethodType,
23
_get_config_dtype_str,
24
)
25
from vllm.model_executor.layers.fused_moe.fused_moe import *
26
+from vllm.model_executor.layers.fused_moe.triton_deep_gemm_moe import (
27
+ TritonOrDeepGemmExperts,
28
+)
29
from vllm.platforms import current_platform
30
from vllm.transformers_utils.config import get_config
31
from vllm.triton_utils import triton
@@ -194,10 +200,33 @@ def benchmark_config(
200
block_shape=block_quant_shape,
201
)
202
203
+ deep_gemm_experts = mk.FusedMoEModularKernel(
204
+ prepare_finalize=MoEPrepareAndFinalizeNoEP(),
205
+ fused_experts=TritonOrDeepGemmExperts(
206
+ moe_config=FusedMoEConfig(
207
+ num_experts=num_experts,
208
+ experts_per_token=topk,
209
+ hidden_dim=hidden_size,
210
+ intermediate_size_per_partition=shard_intermediate_size,
211
+ num_local_experts=num_experts,
212
+ activation="silu",
213
+ parallel_config=FusedMoEParallelConfig.make_no_parallel(),
214
+ in_dtype=init_dtype,
215
+ routing_method=RoutingMethodType.TopK,
216
+ ),
217
+ quant_config=quant_config,
218
+ ),
219
+ )
220
+
221
with override_config(config):
222
topk_weights, topk_ids, token_expert_indices = fused_topk(
223
x, input_gating, topk, renormalize=not use_deep_gemm
224
)
225
+
226
+ if use_deep_gemm:
227
+ return deep_gemm_experts(
228
+ x, w1, w2, topk_weights, topk_ids, inplace=True
229
+ )
230
return fused_experts(
231
x,
232
w1,
@@ -206,7 +235,6 @@ def benchmark_config(
235
topk_ids,
236
inplace=True,
237
quant_config=quant_config,
209
- allow_deep_gemm=use_deep_gemm,
238
)
239
240
# JIT compilation & warmup
docs/design/moe_kernel_features.md
+2
-2
@@ -85,10 +85,10 @@ To be used with a particular `FusedMoEPrepareAndFinalize` subclass, MoE kernels
85
|--------|-------------------|--------------|---------------|---------------------|-----------------------|---------|--------|
86
| triton | standard | all<sup>1</sup> | G,A,T | silu, gelu,</br>swigluoai,</br>silu_no_mul,</br>gelu_no_mul | Y | Y | [`fused_experts`][vllm.model_executor.layers.fused_moe.fused_moe.fused_experts],</br>[`TritonExperts`][vllm.model_executor.layers.fused_moe.fused_moe.TritonExperts] |
87
| triton (batched) | batched | all<sup>1</sup> | G,A,T | silu, gelu | <sup>6</sup> | Y | [`BatchedTritonExperts`][vllm.model_executor.layers.fused_moe.fused_batched_moe.BatchedTritonExperts] |
88
-| deep gemm | standard,</br>batched | fp8 | G(128),A,T | silu, gelu | <sup>6</sup> | Y | [`deep_gemm_moe_fp8`][vllm.model_executor.layers.fused_moe.deep_gemm_moe.deep_gemm_moe_fp8],</br>[`DeepGemmExperts`][vllm.model_executor.layers.fused_moe.deep_gemm_moe.DeepGemmExperts],</br>[`BatchedDeepGemmExperts`][vllm.model_executor.layers.fused_moe.batched_deep_gemm_moe.BatchedDeepGemmExperts] |
88
+| deep gemm | standard,</br>batched | fp8 | G(128),A,T | silu, gelu | <sup>6</sup> | Y | </br>[`DeepGemmExperts`][vllm.model_executor.layers.fused_moe.deep_gemm_moe.DeepGemmExperts],</br>[`BatchedDeepGemmExperts`][vllm.model_executor.layers.fused_moe.batched_deep_gemm_moe.BatchedDeepGemmExperts] |
89
| cutlass_fp4 | standard,</br>batched | nvfp4 | A,T | silu | Y | Y | [`CutlassExpertsFp4`][vllm.model_executor.layers.fused_moe.cutlass_moe.CutlassExpertsFp4] |
90
| cutlass_fp8 | standard,</br>batched | fp8 | A,T | silu, gelu | Y | Y | [`CutlassExpertsFp8`][vllm.model_executor.layers.fused_moe.cutlass_moe.CutlassExpertsFp8],</br>[`CutlasBatchedExpertsFp8`][vllm.model_executor.layers.fused_moe.cutlass_moe.CutlassBatchedExpertsFp8] |
91
-| flashinfer | standard | nvfp4,</br>fp8 | T | <sup>5</sup> | N | Y | [`flashinfer_cutlass_moe_fp4`][vllm.model_executor.layers.fused_moe.flashinfer_cutlass_moe.flashinfer_cutlass_moe_fp4],</br>[`FlashInferExperts`][vllm.model_executor.layers.fused_moe.flashinfer_cutlass_moe.FlashInferExperts] |
91
+| flashinfer | standard | nvfp4,</br>fp8 | T | <sup>5</sup> | N | Y | [`FlashInferExperts`][vllm.model_executor.layers.fused_moe.flashinfer_cutlass_moe.FlashInferExperts] |
92
| gpt oss triton | standard | N/A | N/A | <sup>5</sup> | Y | Y | [`triton_kernel_fused_experts`][vllm.model_executor.layers.fused_moe.gpt_oss_triton_kernels_moe.triton_kernel_fused_experts],</br>[`OAITritonExperts`][vllm.model_executor.layers.fused_moe.gpt_oss_triton_kernels_moe.OAITritonExperts] |
93
| marlin | standard,</br>batched | <sup>3</sup> / N/A | <sup>3</sup> / N/A | silu,</br>swigluoai | Y | Y | [`fused_marlin_moe`][vllm.model_executor.layers.fused_moe.fused_marlin_moe.fused_marlin_moe],</br>[`MarlinExperts`][vllm.model_executor.layers.fused_moe.fused_marlin_moe.MarlinExperts],</br>[`BatchedMarlinExperts`][vllm.model_executor.layers.fused_moe.fused_marlin_moe.BatchedMarlinExperts] |
94
| trtllm | standard | mxfp4,</br>nvfp4 | G(16),G(32) | <sup>5</sup> | N | Y | [`TrtLlmGenExperts`][vllm.model_executor.layers.fused_moe.trtllm_moe.TrtLlmGenExperts] |
tests/compile/test_fusion_attn.py
+3
-3
@@ -43,7 +43,7 @@ from vllm.forward_context import get_forward_context, set_forward_context
43
from vllm.model_executor.layers.quantization.utils.quant_utils import (
44
QuantKey,
45
kFp8StaticTensorSym,
46
- kNvfp4Quant,
46
+ kNvfp4Dynamic,
47
)
48
from vllm.platforms import current_platform
49
from vllm.utils.flashinfer import has_flashinfer
@@ -215,7 +215,7 @@ class TestAttentionFp8StaticQuantPatternModel(AttentionQuantPatternModel):
215
class TestAttentionNvfp4QuantPatternModel(AttentionQuantPatternModel):
216
"""Test model for AttentionNvfp4QuantPattern fusion."""
217
218
- quant_key = kNvfp4Quant
218
+ quant_key = kNvfp4Dynamic
219
220
def __init__(self, *args, **kwargs):
221
super().__init__(*args, **kwargs)
@@ -468,7 +468,7 @@ def test_attention_quant_pattern(
468
469
# Note: for fp8, fully_replaced=False because query quant ops remain in graph.
470
# Only output quant ops are fused into attention.
471
- test_backend.check_before_ops([quant_op], fully_replaced=quant_key is kNvfp4Quant)
471
+ test_backend.check_before_ops([quant_op], fully_replaced=quant_key is kNvfp4Dynamic)
472
473
# access the underlying `AttnFusionPass` on the `LazyInitPass`
474
assert attn_pass.pass_.matched_count == sum(attn_fusion_supported)
tests/compile/test_silu_mul_quant_fusion.py
+3
-3
@@ -44,7 +44,7 @@ from vllm.model_executor.layers.quantization.utils.fp8_utils import W8A8BlockFp8
44
from vllm.model_executor.layers.quantization.utils.quant_utils import (
45
GroupShape,
46
kFp8StaticTensorSym,
47
- kNvfp4Quant,
47
+ kNvfp4Dynamic,
48
)
49
from vllm.platforms import current_platform
50
@@ -134,11 +134,11 @@ class TestSiluMulNvfp4QuantModel(torch.nn.Module):
134
def ops_in_model_before(self):
135
return [
136
SILU_MUL_OP if self.enable_silu_mul_custom_op else torch.ops.aten.mul,
137
- QUANT_OPS[kNvfp4Quant],
137
+ QUANT_OPS[kNvfp4Dynamic],
138
]
139
140
def ops_in_model_after(self):
141
- return [FUSED_OPS[kNvfp4Quant]]
141
+ return [FUSED_OPS[kNvfp4Dynamic]]
142
143
144
class TestSiluMulGroupFp8QuantModel(torch.nn.Module):
tests/evals/gsm8k/configs/moe-refactor-dp-ep/Llama-4-Scout-Fp8-ModelOpt-triton.yaml
+3
@@ -3,3 +3,6 @@ accuracy_threshold: 0.92
3
num_questions: 1319
4
num_fewshot: 5
5
server_args: "--enforce-eager --max-model-len 8192 --data-parallel-size 2 --enable-expert-parallel"
6
+env:
7
+ VLLM_USE_FLASHINFER_MOE_FP8: "0"
8
+ VLLM_USE_DEEP_GEMM: "0"
tests/evals/gsm8k/configs/moe-refactor-dp-ep/Qwen3-30B-A3B-Fp8-AutoFp8-deepgemm-deepep-ll.yaml
-1
@@ -6,4 +6,3 @@ server_args: "--enforce-eager --max-model-len 8192 --data-parallel-size 2 --enab
6
env:
7
VLLM_USE_DEEP_GEMM: "1"
8
VLLM_USE_DEEP_GEMM_MOE: "1"
9
- VLLM_USE_DEEP_GEMM_E8M0: "0"
tests/evals/gsm8k/configs/moe-refactor-dp-ep/Qwen3-30B-A3B-Fp8-CT-Block-deepgemm-deepep-ll.yaml
-1
@@ -6,4 +6,3 @@ server_args: "--enforce-eager --max-model-len 8192 --data-parallel-size 2 --enab
6
env:
7
VLLM_USE_DEEP_GEMM: "1"
8
VLLM_USE_DEEP_GEMM_MOE: "1"
9
- VLLM_USE_DEEP_GEMM_E8M0: "0"
tests/evals/gsm8k/configs/moe-refactor/Llama-4-Scout-Fp8-ModelOpt-triton.yaml
+2
@@ -3,3 +3,5 @@ accuracy_threshold: 0.92
3
num_questions: 1319
4
num_fewshot: 5
5
server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2"
6
+env:
7
+ VLLM_USE_FLASHINFER_MOE_FP8: "0"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-Fp8-AutoFp8-fi-cutlass.yaml
-2
@@ -4,7 +4,5 @@ num_questions: 1319
4
num_fewshot: 5
5
server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2"
6
env:
7
- VLLM_USE_DEEP_GEMM: "0"
8
- VLLM_USE_DEEP_GEMM_MOE: "0"
7
VLLM_USE_FLASHINFER_MOE_FP8: "1"
8
VLLM_FLASHINFER_MOE_BACKEND: "throughput"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-Fp8-AutoFp8-fi-trtllm.yaml
-2
@@ -4,7 +4,5 @@ num_questions: 1319
4
num_fewshot: 5
5
server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2"
6
env:
7
- VLLM_USE_DEEP_GEMM: "0"
8
- VLLM_USE_DEEP_GEMM_MOE: "0"
7
VLLM_USE_FLASHINFER_MOE_FP8: "1"
8
VLLM_FLASHINFER_MOE_BACKEND: "latency"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-Fp8-AutoFp8-marlin.yaml
-2
@@ -4,6 +4,4 @@ num_questions: 1319
4
num_fewshot: 5
5
server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2"
6
env:
7
- VLLM_USE_DEEP_GEMM: "0"
8
- VLLM_USE_DEEP_GEMM_MOE: "0"
7
VLLM_TEST_FORCE_FP8_MARLIN: "1"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-Fp8-AutoFp8-triton.yaml
+1
-1
@@ -4,5 +4,5 @@ num_questions: 1319
4
num_fewshot: 5
5
server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2"
6
env:
7
+ VLLM_USE_FLASHINFER_MOE_FP8: "0"
8
VLLM_USE_DEEP_GEMM: "0"
8
- VLLM_USE_DEEP_GEMM_MOE: "0"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-Fp8-CT-Block-fi-cutlass.yaml
-2
@@ -4,7 +4,5 @@ num_questions: 1319
4
num_fewshot: 5
5
server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2"
6
env:
7
- VLLM_USE_DEEP_GEMM: "0"
8
- VLLM_USE_DEEP_GEMM_MOE: "0"
7
VLLM_USE_FLASHINFER_MOE_FP8: "1"
8
VLLM_FLASHINFER_MOE_BACKEND: "throughput"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-Fp8-CT-Block-marlin.yaml
-2
@@ -4,6 +4,4 @@ num_questions: 1319
4
num_fewshot: 5
5
server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2"
6
env:
7
- VLLM_USE_DEEP_GEMM: "0"
8
- VLLM_USE_DEEP_GEMM_MOE: "0"
7
VLLM_TEST_FORCE_FP8_MARLIN: "1"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-Fp8-CT-Block-triton.yaml
+1
-1
@@ -4,5 +4,5 @@ num_questions: 1319
4
num_fewshot: 5
5
server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2"
6
env:
7
+ VLLM_USE_FLASHINFER_MOE_FP8: "0"
8
VLLM_USE_DEEP_GEMM: "0"
8
- VLLM_USE_DEEP_GEMM_MOE: "0"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-NvFp4-CT-vllm-cutlass.yaml
+2
@@ -3,3 +3,5 @@ accuracy_threshold: 0.88
3
num_questions: 1319
4
num_fewshot: 5
5
server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2"
6
+env:
7
+ VLLM_USE_FLASHINFER_MOE_FP4: "0"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-NvFp4-ModelOpt-vllm-cutlass.yaml
+2
@@ -3,3 +3,5 @@ accuracy_threshold: 0.88
3
num_questions: 1319
4
num_fewshot: 5
5
server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2"
6
+env:
7
+ VLLM_USE_FLASHINFER_MOE_FP4: "0"
tests/kernels/moe/modular_kernel_tools/common.py
+5
@@ -26,6 +26,7 @@ from vllm.model_executor.layers.fused_moe.config import (
26
FusedMoEConfig,
27
FusedMoEParallelConfig,
28
FusedMoEQuantConfig,
29
+ RoutingMethodType,
30
)
31
from vllm.utils.import_utils import has_deep_ep, has_deep_gemm, has_pplx
32
@@ -574,10 +575,14 @@ def make_modular_kernel(
575
num_experts=config.E,
576
experts_per_token=config.topk,
577
hidden_dim=config.K,
578
+ intermediate_size_per_partition=config.N,
579
num_local_experts=config.num_local_experts,
580
moe_parallel_config=moe_parallel_config,
581
in_dtype=config.dtype,
582
max_num_tokens=next_power_of_2(config.M),
583
+ activation="silu",
584
+ device=vllm_config.device_config.device,
585
+ routing_method=RoutingMethodType.DeepSeekV3,
586
)
587
588
# make modular kernel
tests/kernels/moe/modular_kernel_tools/mk_objects.py
+17
-75
@@ -425,84 +425,26 @@ def make_fused_experts(
425
num_dispatchers: int,
426
N: int,
427
) -> mk.FusedMoEPermuteExpertsUnpermute:
428
- batch_kwargs = {
429
- "max_num_tokens": moe.max_num_tokens,
430
- "num_dispatchers": num_dispatchers,
431
- }
432
- quant_kwargs = {
433
- "quant_config": quant_config,
434
- }
435
- deepgemm_kwargs = {"allow_deep_gemm": has_deep_gemm()}
436
-
437
- torch.set_printoptions(threshold=0, edgeitems=0, linewidth=10000)
438
-
439
- if fused_experts_type == BatchedDeepGemmExperts:
440
- kwargs = batch_kwargs | quant_kwargs
441
- print(f"Making BatchedDeepGemmExperts {kwargs} ...")
442
- experts = BatchedDeepGemmExperts(**kwargs)
443
- elif fused_experts_type == BatchedTritonExperts:
444
- kwargs = batch_kwargs | quant_kwargs
445
- print(f"Making BatchedTritonExperts {kwargs} ...")
446
- experts = BatchedTritonExperts(**kwargs)
447
- elif fused_experts_type == DeepGemmExperts:
448
- print(f"Making DeepGemmExperts {quant_config} ...")
449
- experts = DeepGemmExperts(quant_config)
450
- elif fused_experts_type == TritonExperts:
451
- kwargs = quant_kwargs
452
- print(f"Making TritonExperts {kwargs} ...")
453
- experts = TritonExperts(**kwargs)
454
- elif fused_experts_type == TritonOrDeepGemmExperts:
455
- kwargs = quant_kwargs | deepgemm_kwargs
456
- print(f"Making TritonOrDeepGemmExperts {kwargs} ...")
457
- experts = TritonOrDeepGemmExperts(**kwargs)
458
- elif fused_experts_type == NaiveBatchedExperts:
459
- kwargs = batch_kwargs | quant_kwargs
460
- print(f"Making NaiveBatchedExperts {kwargs} ...")
461
- experts = NaiveBatchedExperts(**kwargs)
462
- elif fused_experts_type == CutlassExpertsFp8:
463
- strides = make_cutlass_strides(moe.num_experts, N, moe.hidden_dim)
464
- kwargs = {
465
- "out_dtype": moe.in_dtype,
466
- "ab_strides1": strides[0],
467
- "ab_strides2": strides[1],
468
- "c_strides1": strides[2],
469
- "c_strides2": strides[3],
470
- } | quant_kwargs
471
- print(f"Making CutlassExpertsFp8 {kwargs} ...")
472
- experts = CutlassExpertsFp8(**kwargs)
473
- elif fused_experts_type == CutlassBatchedExpertsFp8:
474
- strides = make_cutlass_strides(moe.num_experts, N, moe.hidden_dim)
475
- kwargs = {
476
- "max_experts_per_worker": moe.num_local_experts,
477
- "num_dispatchers": num_dispatchers,
478
- "out_dtype": moe.in_dtype,
479
- "ab_strides1": strides[0],
480
- "ab_strides2": strides[1],
481
- "c_strides1": strides[2],
482
- "c_strides2": strides[3],
483
- } | quant_kwargs
484
- print(f"Making CutlassBatchedExpertsFp8 {kwargs} ...")
485
- experts = CutlassBatchedExpertsFp8(**kwargs)
486
- elif fused_experts_type == CutlassExpertsFp4:
428
+ if (
429
+ fused_experts_type.activation_format()
430
+ == mk.FusedMoEActivationFormat.BatchedExperts
431
+ ):
432
kwargs = {
488
- "max_experts_per_worker": moe.num_local_experts,
433
+ "moe_config": moe,
434
+ "quant_config": quant_config,
435
+ "max_num_tokens": moe.max_num_tokens,
436
"num_dispatchers": num_dispatchers,
490
- "out_dtype": moe.in_dtype,
491
- } | quant_kwargs
492
- print(f"Making CutlassExpertsFp4 {kwargs} ...")
493
- experts = CutlassExpertsFp4(**kwargs)
494
- elif fused_experts_type == FlashInferExperts:
495
- kwargs = {
496
- "out_dtype": moe.in_dtype,
497
- "ep_rank": moe.ep_rank,
498
- "ep_size": moe.ep_size,
499
- "tp_rank": moe.tp_rank,
500
- "tp_size": moe.tp_size,
501
- } | quant_kwargs
502
- print(f"Making FlashInferExperts {kwargs} ...")
503
- experts = FlashInferExperts(**kwargs)
437
+ }
438
else:
505
- raise RuntimeError(f"Unknown fused experts type: {fused_experts_type}")
439
+ kwargs = {
440
+ "moe_config": moe,
441
+ "quant_config": quant_config,
442
+ }
443
+
444
+ torch.set_printoptions(threshold=0, edgeitems=0, linewidth=10000)
445
+
446
+ print(f"Making {fused_experts_type.__class__.__name__} {kwargs} ...")
447
+ experts = fused_experts_type(**kwargs)
448
449
torch.set_printoptions(threshold=1000, edgeitems=5, linewidth=80)
450
tests/kernels/moe/test_batched_deepgemm.py
+3
@@ -16,6 +16,7 @@ from vllm.model_executor.layers.fused_moe.modular_kernel import FusedMoEModularK
16
from vllm.utils.deep_gemm import calc_diff, is_deep_gemm_supported
17
18
from .test_deepgemm import make_block_quant_fp8_weights
19
+from .utils import make_dummy_moe_config
20
21
BLOCK_SIZE = [128, 128]
22
@@ -71,6 +72,7 @@ def test_batched_deepgemm_vs_triton(
72
max_num_tokens=max_num_tokens,
73
num_dispatchers=1,
74
quant_config=quant_config,
75
+ moe_config=make_dummy_moe_config(),
76
)
77
mk_triton = FusedMoEModularKernel(prep_finalize, triton_experts)
78
@@ -89,6 +91,7 @@ def test_batched_deepgemm_vs_triton(
91
max_num_tokens=max_num_tokens,
92
num_dispatchers=1,
93
quant_config=quant_config,
94
+ moe_config=make_dummy_moe_config(),
95
)
96
mk_deepgemm = FusedMoEModularKernel(prep_finalize, deepgemm_experts)
97
tests/kernels/moe/test_block_fp8.py
+39
-3
@@ -4,7 +4,12 @@
4
import pytest
5
import torch
6
7
-from tests.kernels.moe.utils import make_test_quant_config, make_test_weights
7
+import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8
+from tests.kernels.moe.utils import (
9
+ make_dummy_moe_config,
10
+ make_test_quant_config,
11
+ make_test_weights,
12
+)
13
from tests.kernels.quant_utils import (
14
native_per_token_group_quant_fp8,
15
native_w8a8_block_matmul,
@@ -15,13 +20,21 @@ from vllm.model_executor.layers.fused_moe import (
20
fused_experts,
21
fused_topk,
22
)
23
+from vllm.model_executor.layers.fused_moe.config import (
24
+ fp8_w8a8_moe_quant_config,
25
+)
26
from vllm.model_executor.layers.fused_moe.deep_gemm_moe import (
27
_valid_deep_gemm_shape,
20
- deep_gemm_moe_fp8,
28
)
29
from vllm.model_executor.layers.fused_moe.fused_moe import (
30
modular_triton_fused_moe,
31
)
32
+from vllm.model_executor.layers.fused_moe.prepare_finalize import (
33
+ MoEPrepareAndFinalizeNoEP,
34
+)
35
+from vllm.model_executor.layers.fused_moe.triton_deep_gemm_moe import (
36
+ TritonOrDeepGemmExperts,
37
+)
38
from vllm.platforms import current_platform
39
from vllm.utils.deep_gemm import (
40
get_mk_alignment_for_contiguous_layout,
@@ -161,7 +174,7 @@ def test_w8a8_block_fp8_fused_moe(
174
block_shape=block_size,
175
)
176
164
- m_fused_moe = modular_triton_fused_moe(quant_config)
177
+ m_fused_moe = modular_triton_fused_moe(make_dummy_moe_config(), quant_config)
178
179
topk_weights, topk_ids, _ = fused_topk(a, score.float(), topk, False)
180
@@ -236,6 +249,29 @@ def test_w8a8_block_fp8_deep_gemm_fused_moe(M, N, K, E, topk, seed, monkeypatch)
249
250
topk_weights, topk_ids, _ = fused_topk(a, score.float(), topk, False)
251
252
+ quant_config = fp8_w8a8_moe_quant_config(
253
+ w1_scale=w1_s,
254
+ w2_scale=w2_s,
255
+ block_shape=block_size,
256
+ )
257
+
258
+ deep_gemm_experts = mk.FusedMoEModularKernel(
259
+ prepare_finalize=MoEPrepareAndFinalizeNoEP(),
260
+ fused_experts=TritonOrDeepGemmExperts(
261
+ moe_config=make_dummy_moe_config(),
262
+ quant_config=quant_config,
263
+ ),
264
+ )
265
+
266
+ def deep_gemm_moe_fp8(a, w1, w2, w1_s, w2_s, topk_weights, topk_ids):
267
+ return deep_gemm_experts(
268
+ hidden_states=a,
269
+ w1=w1,
270
+ w2=w2,
271
+ topk_weights=topk_weights,
272
+ topk_ids=topk_ids,
273
+ )
274
+
275
# Set the context to avoid lots of warning spam.
276
with set_current_vllm_config(vllm_config):
277
ref_out = torch_w8a8_block_fp8_moe(
tests/kernels/moe/test_cutlass_moe.py
+16
-13
@@ -8,6 +8,7 @@ import pytest
8
import torch
9
10
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
11
+from tests.kernels.moe.utils import make_dummy_moe_config
12
from vllm import _custom_ops as ops
13
from vllm.config import ParallelConfig, VllmConfig, set_current_vllm_config
14
from vllm.model_executor.layers.fused_moe import fused_experts, fused_topk
@@ -193,16 +194,18 @@ def run_with_expert_maps(
194
195
out_tensor = torch.zeros_like(cutlass_moe_kwargs["hidden_states"])
196
for kwargs, new_quant_config in slice_experts():
197
+ w2 = kwargs["w2"]
198
+ a = kwargs["hidden_states"]
199
kernel = mk.FusedMoEModularKernel(
200
MoEPrepareAndFinalizeNoEP(),
201
CutlassExpertsFp8(
199
- out_dtype=kwargs["hidden_states"].dtype,
200
- # NOTE(rob): w2 is shaped as [E, hidden, intermediate]
201
- e=kwargs["w2"].shape[0], # type: ignore[union-attr]
202
- n=kwargs["w2"].shape[2], # type: ignore[union-attr]
203
- k=kwargs["w2"].shape[1], # type: ignore[union-attr]
202
+ moe_config=make_dummy_moe_config(
203
+ num_experts=w2.shape[0],
204
+ hidden_dim=w2.shape[1],
205
+ intermediate_size_per_partition=w2.shape[2],
206
+ in_dtype=a.dtype,
207
+ ),
208
quant_config=new_quant_config,
205
- device="cuda",
209
),
210
)
211
out_tensor = out_tensor + kernel(**kwargs)
@@ -249,19 +252,19 @@ def run_8_bit(
252
"topk_ids": topk_ids,
253
}
254
252
- num_experts = moe_tensors.w1.size(0)
255
+ num_experts = moe_tensors.w1.size(0) # type: ignore[attr-defined]
256
with_ep = num_local_experts is not None or num_local_experts == num_experts
257
if not with_ep:
258
kernel = mk.FusedMoEModularKernel(
259
MoEPrepareAndFinalizeNoEP(),
260
CutlassExpertsFp8(
258
- out_dtype=moe_tensors.a.dtype,
259
- # NOTE(rob): w2 is shaped as [E, hidden, intermediate]
260
- e=moe_tensors.w2_q.shape[0], # type: ignore[union-attr]
261
- n=moe_tensors.w2_q.shape[2], # type: ignore[union-attr]
262
- k=moe_tensors.w2_q.shape[1], # type: ignore[union-attr]
261
+ moe_config=make_dummy_moe_config(
262
+ num_experts=moe_tensors.w2_q.shape[0], # type: ignore[union-attr]
263
+ hidden_dim=moe_tensors.w2_q.shape[1], # type: ignore[union-attr]
264
+ intermediate_size_per_partition=moe_tensors.w2_q.shape[2], # type: ignore[union-attr]
265
+ in_dtype=moe_tensors.a.dtype,
266
+ ),
267
quant_config=quant_config,
264
- device="cuda",
268
),
269
)
270
return kernel(**kwargs)
tests/kernels/moe/test_deepep_deepgemm_moe.py
+6
-5
@@ -33,7 +33,7 @@ from vllm.v1.worker.workspace import init_workspace_manager
33
34
from ...utils import multi_gpu_test
35
from .parallel_utils import ProcessGroupInfo, parallel_launch
36
-from .utils import make_test_weights
36
+from .utils import make_dummy_moe_config, make_test_weights
37
38
if has_deep_ep():
39
from vllm.model_executor.layers.fused_moe.deepep_ht_prepare_finalize import (
@@ -192,6 +192,7 @@ def make_ll_modular_kernel(
192
max_num_tokens=max_tokens_per_rank,
193
num_dispatchers=pgi.world_size // dp_size,
194
quant_config=quant_config,
195
+ moe_config=make_dummy_moe_config(),
196
)
197
mk = FusedMoEModularKernel(prepare_finalize=a2a, fused_experts=fused_experts)
198
return mk
@@ -219,7 +220,10 @@ def make_ht_modular_kernel(
220
block_shape=test_config.block_size,
221
)
222
222
- fused_experts = DeepGemmExperts(quant_config)
223
+ fused_experts = DeepGemmExperts(
224
+ moe_config=make_dummy_moe_config(),
225
+ quant_config=quant_config,
226
+ )
227
mk = FusedMoEModularKernel(prepare_finalize=a2a, fused_experts=fused_experts)
228
return mk
229
@@ -349,9 +353,6 @@ def triton_impl(
353
topk_ids=topk_ids,
354
inplace=False,
355
quant_config=quant_config,
352
- # Make sure this is set to False so we
353
- # don't end up comparing the same implementation.
354
- allow_deep_gemm=False,
356
)
357
358
tests/kernels/moe/test_deepep_moe.py
+11
-2
@@ -10,11 +10,14 @@ import pytest
10
import torch.distributed
11
from torch.distributed import ProcessGroup
12
13
+from tests.kernels.moe.utils import make_dummy_moe_config
14
from vllm import _custom_ops as ops
15
from vllm.config import VllmConfig, set_current_vllm_config
16
from vllm.model_executor.layers.activation import SiluAndMul
17
from vllm.model_executor.layers.fused_moe import TritonExperts
17
-from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
18
+from vllm.model_executor.layers.fused_moe.config import (
19
+ FusedMoEQuantConfig,
20
+)
21
from vllm.model_executor.layers.fused_moe.fused_batched_moe import BatchedTritonExperts
22
from vllm.model_executor.layers.fused_moe.modular_kernel import FusedMoEModularKernel
23
from vllm.model_executor.layers.quantization.utils.fp8_utils import (
@@ -160,15 +163,21 @@ def make_modular_kernel(
163
164
num_dispatchers = pgi.world_size // dp_size
165
166
+ moe_config = make_dummy_moe_config()
167
+
168
if low_latency_mode:
169
assert not quant_config.per_act_token_quant, "not supported in ll mode"
170
fused_experts = BatchedTritonExperts(
171
max_num_tokens=MAX_TOKENS_PER_RANK,
172
num_dispatchers=num_dispatchers,
173
+ moe_config=moe_config,
174
quant_config=quant_config,
175
)
176
else:
171
- fused_experts = TritonExperts(quant_config=quant_config)
177
+ fused_experts = TritonExperts(
178
+ moe_config=moe_config,
179
+ quant_config=quant_config,
180
+ )
181
182
mk = FusedMoEModularKernel(prepare_finalize=a2a, fused_experts=fused_experts)
183
return mk
tests/kernels/moe/test_deepgemm.py
+26
-13
@@ -11,10 +11,19 @@ import math
11
import pytest
12
import torch
13
14
-from vllm.model_executor.layers.fused_moe.config import fp8_w8a8_moe_quant_config
15
-
14
# vLLM fused-expert reference (Triton fallback + DeepGEMM option)
15
+import vllm.model_executor.layers.fused_moe.modular_kernel as mk
16
+from tests.kernels.moe.utils import make_dummy_moe_config
17
+from vllm.model_executor.layers.fused_moe.config import (
18
+ fp8_w8a8_moe_quant_config,
19
+)
20
from vllm.model_executor.layers.fused_moe.fused_moe import fused_experts
21
+from vllm.model_executor.layers.fused_moe.prepare_finalize import (
22
+ MoEPrepareAndFinalizeNoEP,
23
+)
24
+from vllm.model_executor.layers.fused_moe.triton_deep_gemm_moe import (
25
+ TritonOrDeepGemmExperts,
26
+)
27
from vllm.model_executor.layers.quantization.utils.fp8_utils import (
28
per_token_group_quant_fp8,
29
)
@@ -100,6 +109,14 @@ def run_single_case(m, n, k, topk, num_experts, block_size):
109
block_shape=block_size,
110
)
111
112
+ deep_gemm_experts = mk.FusedMoEModularKernel(
113
+ prepare_finalize=MoEPrepareAndFinalizeNoEP(),
114
+ fused_experts=TritonOrDeepGemmExperts(
115
+ moe_config=make_dummy_moe_config(),
116
+ quant_config=quant_config,
117
+ ),
118
+ )
119
+
120
# triton reference
121
out_triton = fused_experts(
122
hidden_states=tokens_bf16,
@@ -109,19 +126,16 @@ def run_single_case(m, n, k, topk, num_experts, block_size):
126
topk_ids=topk_ids,
127
inplace=False,
128
quant_config=quant_config,
112
- allow_deep_gemm=False,
129
)
130
131
# DeepGemm
116
- out_deepgemm = fused_experts(
132
+ out_deepgemm = deep_gemm_experts(
133
hidden_states=tokens_bf16,
134
w1=w1,
135
w2=w2,
136
topk_weights=topk_weights,
137
topk_ids=topk_ids,
138
inplace=False,
123
- quant_config=quant_config,
124
- allow_deep_gemm=True,
139
)
140
diff = calc_diff(out_deepgemm, out_triton)
141
assert diff < 0.001, f"Diff exceeded 1%: {diff}"
@@ -147,20 +161,19 @@ def test_deepgemm_vs_triton(m, n, k, topk, num_experts, monkeypatch, workspace_i
161
with monkeypatch.context() as mp:
162
mp.setenv("VLLM_USE_DEEP_GEMM", "1")
163
150
- _fused_moe_mod = importlib.import_module(
151
- "vllm.model_executor.layers.fused_moe.fused_moe"
152
- )
164
+ _DeepGemmExperts = importlib.import_module(
165
+ "vllm.model_executor.layers.fused_moe.deep_gemm_moe"
166
+ ).DeepGemmExperts
167
168
call_counter = {"cnt": 0}
169
156
- orig_fn = _fused_moe_mod.deep_gemm_moe_fp8
170
+ orig_fn = _DeepGemmExperts.apply
171
158
- def _spy_deep_gemm_moe_fp8(*args, **kwargs):
172
+ def _spy_apply(*args, **kwargs):
173
call_counter["cnt"] += 1
174
return orig_fn(*args, **kwargs)
175
162
- monkeypatch.setattr(_fused_moe_mod, "deep_gemm_moe_fp8", _spy_deep_gemm_moe_fp8)
163
-
176
+ monkeypatch.setattr(_DeepGemmExperts, "apply", _spy_apply)
177
if topk > num_experts:
178
pytest.skip(f"topk={topk} > num_experts={num_experts}")
179
tests/kernels/moe/test_flashinfer.py
+25
-20
@@ -8,7 +8,10 @@ import torch
8
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
9
from vllm.config import ParallelConfig, VllmConfig, set_current_vllm_config
10
from vllm.model_executor.layers.fused_moe.config import (
11
+ FusedMoEConfig,
12
+ FusedMoEParallelConfig,
13
FusedMoEQuantConfig,
14
+ RoutingMethodType,
15
fp8_w8a8_moe_quant_config,
16
)
17
from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_moe import (
@@ -116,18 +119,7 @@ class TestData:
119
layer.w13_weight_scale = w13_weight_scale
120
layer.w2_weight_scale = w2_weight_scale
121
# Setup dummy config.
119
- layer.moe_parallel_config = mk.FusedMoEParallelConfig(
120
- tp_size=1,
121
- pcp_size=1,
122
- dp_size=1,
123
- ep_size=1,
124
- tp_rank=0,
125
- pcp_rank=0,
126
- dp_rank=0,
127
- ep_rank=0,
128
- use_ep=False,
129
- all2all_backend="naive",
130
- )
122
+ layer.moe_parallel_config = mk.FusedMoEParallelConfig.make_no_parallel()
123
124
# flashinfer expects swapped rows for w13
125
layer.w13_weight.data = swap_w13_to_w31(layer.w13_weight.data)
@@ -238,6 +230,8 @@ def test_flashinfer_cutlass_moe_fp8_no_graph(
230
):
231
set_random_seed(7)
232
monkeypatch.setenv("VLLM_FUSED_MOE_CHUNK_SIZE", "8192")
233
+ assert activation in ["silu", "relu2_no_mul"]
234
+ is_act_and_mul = activation == "silu_and_mul"
235
with set_current_vllm_config(vllm_config):
236
td = TestData.make_moe_tensors_8bit(
237
m, k, n, e, is_trtllm=False, activation=activation
@@ -285,19 +279,30 @@ def test_flashinfer_cutlass_moe_fp8_no_graph(
279
td.layer.get_fused_moe_quant_config = get_fused_moe_quant_config
280
td.layer.quant_method = td.layer
281
282
+ moe_config = FusedMoEConfig(
283
+ num_experts=e,
284
+ experts_per_token=topk,
285
+ hidden_dim=k,
286
+ intermediate_size_per_partition=n,
287
+ num_local_experts=e,
288
+ activation=activation,
289
+ device="cuda",
290
+ moe_parallel_config=FusedMoEParallelConfig.make_no_parallel(),
291
+ in_dtype=torch.bfloat16,
292
+ is_act_and_mul=is_act_and_mul,
293
+ routing_method=RoutingMethodType.TopK,
294
+ )
295
+
296
kernel = mk.FusedMoEModularKernel(
297
MoEPrepareAndFinalizeNoEP(
290
- defer_input_quant=quant_config.is_block_quantized
298
+ defer_input_quant=FlashInferExperts.expects_unquantized_inputs(
299
+ moe_config=moe_config,
300
+ quant_config=quant_config,
301
+ )
302
),
303
FlashInferExperts(
293
- out_dtype=td.layer.orig_dtype,
304
+ moe_config=moe_config,
305
quant_config=quant_config,
295
- ep_rank=td.layer.moe_parallel_config.ep_rank,
296
- ep_size=td.layer.moe_parallel_config.ep_size,
297
- tp_rank=td.layer.moe_parallel_config.tp_rank,
298
- tp_size=td.layer.moe_parallel_config.tp_size,
299
- use_dp=False,
300
- use_deepseek_fp8_block_scale=False,
306
),
307
)
308
tests/kernels/moe/test_flashinfer_moe.py
+29
-5
@@ -13,14 +13,19 @@ from tests.kernels.utils import torch_moe
13
from vllm import _custom_ops as ops
14
from vllm.config import ParallelConfig, VllmConfig, set_current_vllm_config
15
from vllm.model_executor.layers.fused_moe import fused_topk
16
+from vllm.model_executor.layers.fused_moe.config import (
17
+ FusedMoEConfig,
18
+ FusedMoEParallelConfig,
19
+ RoutingMethodType,
20
+)
21
from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_moe import (
22
FlashInferExperts,
23
is_valid_flashinfer_cutlass_fused_moe,
24
)
20
-from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_prepare_finalize import (
21
- create_flashinfer_prepare_finalize,
22
-)
25
from vllm.model_executor.layers.fused_moe.modular_kernel import FusedMoEModularKernel
26
+from vllm.model_executor.layers.fused_moe.prepare_finalize import (
27
+ MoEPrepareAndFinalizeNoEP,
28
+)
29
from vllm.platforms import current_platform
30
from vllm.utils.flashinfer import has_flashinfer_cutlass_fused_moe
31
from vllm.utils.torch_utils import set_random_seed
@@ -86,9 +91,28 @@ def test_flashinfer_fp4_moe_no_graph(
91
92
assert is_valid_flashinfer_cutlass_fused_moe(a, w1_q, w2_q)
93
94
+ moe_config = FusedMoEConfig(
95
+ num_experts=e,
96
+ experts_per_token=topk,
97
+ hidden_dim=k,
98
+ intermediate_size_per_partition=n,
99
+ num_local_experts=e,
100
+ activation=activation,
101
+ device="cuda",
102
+ moe_parallel_config=FusedMoEParallelConfig.make_no_parallel(),
103
+ in_dtype=dtype,
104
+ is_act_and_mul=is_gated_act,
105
+ routing_method=RoutingMethodType.TopK,
106
+ )
107
+
108
flashinfer_experts = FusedMoEModularKernel(
90
- create_flashinfer_prepare_finalize(use_dp=False, use_nvfp4=True),
91
- FlashInferExperts(out_dtype=dtype, quant_config=quant_config),
109
+ MoEPrepareAndFinalizeNoEP(
110
+ defer_input_quant=FlashInferExperts.expects_unquantized_inputs(
111
+ moe_config=moe_config,
112
+ quant_config=quant_config,
113
+ )
114
+ ),
115
+ FlashInferExperts(moe_config=moe_config, quant_config=quant_config),
116
)
117
118
fi_activation = {"silu_and_mul": "silu", "relu2": "relu2_no_mul"}[activation]
tests/kernels/moe/test_modular_oai_triton_moe.py
+4
-2
@@ -36,6 +36,8 @@ from vllm.model_executor.layers.utils import shuffle_weight
36
from vllm.platforms import current_platform
37
from vllm.utils.torch_utils import set_random_seed
38
39
+from .utils import make_dummy_moe_config
40
+
41
MNK = [
42
(1, 512, 384),
43
(1, 2880, 2880),
@@ -174,9 +176,9 @@ def oai_triton_moe_impl(
176
)
177
178
if unfused:
177
- fused_experts = UnfusedOAITritonExperts(quant_config)
179
+ fused_experts = UnfusedOAITritonExperts(make_dummy_moe_config(), quant_config)
180
else:
179
- fused_experts = OAITritonExperts(quant_config)
181
+ fused_experts = OAITritonExperts(make_dummy_moe_config(), quant_config)
182
183
mk = FusedMoEModularKernel(MoEPrepareAndFinalizeNoEP(), fused_experts)
184
tests/kernels/moe/test_moe.py
+3
-3
@@ -18,7 +18,7 @@ from transformers import MixtralConfig
18
from transformers.models.mixtral.modeling_mixtral import MixtralSparseMoeBlock
19
20
import vllm.model_executor.layers.fused_moe # noqa
21
-from tests.kernels.moe.utils import fused_moe
21
+from tests.kernels.moe.utils import fused_moe, make_dummy_moe_config
22
from tests.kernels.utils import opcheck, stack_and_dev, torch_experts, torch_moe
23
from vllm._aiter_ops import rocm_aiter_ops
24
from vllm.config import VllmConfig, set_current_vllm_config
@@ -332,7 +332,7 @@ def test_fused_moe(
332
#
333
quant_config = FUSED_MOE_UNQUANTIZED_CONFIG
334
335
- m_fused_moe_fn = modular_triton_fused_moe(quant_config)
335
+ m_fused_moe_fn = modular_triton_fused_moe(make_dummy_moe_config(), quant_config)
336
337
def m_fused_moe(
338
a: torch.Tensor,
@@ -437,7 +437,7 @@ def test_naive_block_assignment_moe(
437
#
438
quant_config = FUSED_MOE_UNQUANTIZED_CONFIG
439
440
- m_fused_moe_fn = modular_triton_fused_moe(quant_config)
440
+ m_fused_moe_fn = modular_triton_fused_moe(make_dummy_moe_config(), quant_config)
441
442
def m_fused_moe(
443
a: torch.Tensor,
tests/kernels/moe/test_nvfp4_moe.py
+2
-3
@@ -4,7 +4,7 @@ import pytest
4
import torch
5
6
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
-from tests.kernels.moe.utils import make_test_weights
7
+from tests.kernels.moe.utils import make_dummy_moe_config, make_test_weights
8
from tests.kernels.quantization.nvfp4_utils import (
9
FLOAT4_E2M1_MAX,
10
FLOAT8_E4M3_MAX,
@@ -92,8 +92,7 @@ def test_cutlass_fp4_moe_no_graph(
92
kernel = mk.FusedMoEModularKernel(
93
MoEPrepareAndFinalizeNoEP(defer_input_quant=True),
94
CutlassExpertsFp4(
95
- out_dtype=dtype,
96
- max_experts_per_worker=e,
95
+ moe_config=make_dummy_moe_config(),
96
quant_config=quant_config,
97
),
98
)
tests/kernels/moe/test_pplx_cutlass_moe.py
+26
-21
@@ -9,12 +9,18 @@ from tests.kernels.utils import torch_experts
9
from vllm import _custom_ops as ops
10
from vllm.config import VllmConfig, set_current_vllm_config
11
from vllm.model_executor.layers.fused_moe import fused_topk
12
-from vllm.model_executor.layers.fused_moe.config import fp8_w8a8_moe_quant_config
12
+from vllm.model_executor.layers.fused_moe.config import (
13
+ FusedMoEConfig,
14
+ FusedMoEParallelConfig,
15
+ RoutingMethodType,
16
+ fp8_w8a8_moe_quant_config,
17
+)
18
from vllm.model_executor.layers.fused_moe.cutlass_moe import CutlassBatchedExpertsFp8
19
from vllm.model_executor.layers.fused_moe.modular_kernel import FusedMoEModularKernel
20
from vllm.platforms import current_platform
21
from vllm.utils.math_utils import cdiv
22
from vllm.utils.torch_utils import set_random_seed
23
+from vllm.v1.worker.workspace import init_workspace_manager
24
25
from ...utils import multi_gpu_test
26
from .parallel_utils import ProcessGroupInfo, parallel_launch
@@ -79,6 +85,8 @@ def pplx_cutlass_moe(
85
PplxPrepareAndFinalize,
86
)
87
88
+ init_workspace_manager(torch.cuda.current_device())
89
+
90
assert torch.cuda.current_device() == pgi.local_rank
91
92
num_tokens, hidden_dim = a.shape
@@ -132,28 +140,23 @@ def pplx_cutlass_moe(
140
num_dispatchers=num_dispatchers,
141
)
142
135
- ab_strides1 = torch.full(
136
- (num_local_experts,), hidden_dim, device="cuda", dtype=torch.int64
137
- )
138
- ab_strides2 = torch.full(
139
- (num_local_experts,), intermediate_dim, device="cuda", dtype=torch.int64
140
- )
141
- c_strides1 = torch.full(
142
- (num_local_experts,), 2 * intermediate_dim, device="cuda", dtype=torch.int64
143
- )
144
- c_strides2 = torch.full(
145
- (num_local_experts,), hidden_dim, device="cuda", dtype=torch.int64
146
- )
143
+ def make_moe_config() -> FusedMoEConfig:
144
+ return FusedMoEConfig(
145
+ num_experts=num_experts,
146
+ experts_per_token=topk,
147
+ hidden_dim=hidden_dim,
148
+ intermediate_size_per_partition=intermediate_dim,
149
+ num_local_experts=num_local_experts,
150
+ moe_parallel_config=FusedMoEParallelConfig.make_no_parallel(),
151
+ activation="silu",
152
+ in_dtype=torch.bfloat16,
153
+ device="cuda",
154
+ routing_method=RoutingMethodType.Llama4,
155
+ )
156
157
experts = CutlassBatchedExpertsFp8(
149
- num_local_experts,
150
- num_dispatchers,
151
- out_dtype,
152
- ab_strides1,
153
- ab_strides2,
154
- c_strides1,
155
- c_strides2,
156
- fp8_w8a8_moe_quant_config(
158
+ moe_config=make_moe_config(),
159
+ quant_config=fp8_w8a8_moe_quant_config(
160
per_act_token_quant=per_act_token,
161
per_out_ch_quant=per_out_ch,
162
w1_scale=chunk_by_rank(w1_scale, rank, world_size),
@@ -162,6 +165,8 @@ def pplx_cutlass_moe(
165
if per_act_token
166
else a1_scale[rank],
167
),
168
+ max_num_tokens=max_num_tokens,
169
+ num_dispatchers=num_dispatchers,
170
)
171
172
fused_cutlass_experts = FusedMoEModularKernel(
tests/kernels/moe/test_pplx_moe.py
+2
@@ -29,6 +29,7 @@ except ImportError:
29
30
from tests.kernels.moe.modular_kernel_tools.parallel_utils import _set_vllm_config
31
from tests.kernels.moe.utils import (
32
+ make_dummy_moe_config,
33
make_shared_experts,
34
make_test_weights,
35
naive_batched_moe,
@@ -584,6 +585,7 @@ def pplx_moe(
585
max_num_tokens=max_num_tokens,
586
num_dispatchers=prepare_finalize.num_dispatchers(),
587
quant_config=quant_config,
588
+ moe_config=make_dummy_moe_config(),
589
)
590
591
fused_experts = FusedMoEModularKernel(
tests/kernels/moe/test_routing.py
-7
@@ -6,7 +6,6 @@ import pytest
6
import torch
7
8
from vllm.distributed.eplb.eplb_state import EplbLayerState
9
-from vllm.model_executor.layers.fused_moe.config import RoutingMethodType
9
from vllm.model_executor.layers.fused_moe.router.router_factory import (
10
create_fused_moe_router,
11
)
@@ -385,17 +384,11 @@ def test_grouped_topk(
384
global_num_experts,
385
)
386
388
- routing_method_type = None
389
- if scoring_func == "llama4":
390
- routing_method_type = RoutingMethodType.Llama4
391
- scoring_func = "sigmoid"
392
-
387
router = create_fused_moe_router(
388
use_grouped_topk=True,
389
num_expert_group=num_expert_group,
390
topk_group=topk_group,
391
scoring_func=scoring_func,
398
- routing_method_type=routing_method_type,
392
e_score_correction_bias=e_score_correction_bias,
393
routed_scaling_factor=routed_scaling_factor,
394
top_k=top_k,
tests/kernels/moe/test_triton_moe_no_act_mul.py
+13
-3
@@ -10,6 +10,7 @@ equals N (not N // 2 like gated activations).
10
import pytest
11
import torch
12
13
+from tests.kernels.moe.utils import make_dummy_moe_config
14
from vllm.model_executor.layers.fused_moe.config import (
15
FUSED_MOE_UNQUANTIZED_CONFIG,
16
)
@@ -78,7 +79,10 @@ def test_triton_experts_no_mul_activation(
79
m, n, k, NUM_EXPERTS, topk
80
)
81
81
- experts = TritonExperts(FUSED_MOE_UNQUANTIZED_CONFIG)
82
+ experts = TritonExperts(
83
+ moe_config=make_dummy_moe_config(),
84
+ quant_config=FUSED_MOE_UNQUANTIZED_CONFIG,
85
+ )
86
87
ws1_shape, ws2_shape, out_shape = experts.workspace_shapes(
88
M=m,
@@ -151,7 +155,10 @@ def test_workspace_shapes_no_mul_vs_gated():
155
156
M, N, K, topk = 64, 256, 128, 2
157
154
- experts = TritonExperts(FUSED_MOE_UNQUANTIZED_CONFIG)
158
+ experts = TritonExperts(
159
+ moe_config=make_dummy_moe_config(),
160
+ quant_config=FUSED_MOE_UNQUANTIZED_CONFIG,
161
+ )
162
163
ws1_no_mul, _, out_no_mul = experts.workspace_shapes(
164
M, N, K, topk, 8, 8, None, SILU_NO_MUL
@@ -187,7 +194,10 @@ def test_adjust_n_for_activation():
194
"""Test the adjust_N_for_activation method."""
195
from vllm.model_executor.layers.fused_moe.fused_moe import TritonExperts
196
190
- experts = TritonExperts(FUSED_MOE_UNQUANTIZED_CONFIG)
197
+ experts = TritonExperts(
198
+ moe_config=make_dummy_moe_config(),
199
+ quant_config=FUSED_MOE_UNQUANTIZED_CONFIG,
200
+ )
201
202
N = 256
203
tests/kernels/moe/utils.py
+36
-1
@@ -8,7 +8,12 @@ from tests.kernels.quant_utils import per_block_cast_to_int8
8
from tests.kernels.quantization.nvfp4_utils import FLOAT4_E2M1_MAX, FLOAT8_E4M3_MAX
9
from vllm.model_executor.layers.activation import SiluAndMul
10
from vllm.model_executor.layers.fused_moe import fused_experts, fused_topk
11
-from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
11
+from vllm.model_executor.layers.fused_moe.config import (
12
+ FusedMoEConfig,
13
+ FusedMoEParallelConfig,
14
+ FusedMoEQuantConfig,
15
+ RoutingMethodType,
16
+)
17
from vllm.model_executor.layers.fused_moe.fused_batched_moe import (
18
BatchedPrepareAndFinalize,
19
BatchedTritonExperts,
@@ -20,6 +25,34 @@ from vllm.utils.deep_gemm import per_block_cast_to_fp8
25
from vllm.utils.math_utils import round_up
26
27
28
+def make_dummy_moe_config(
29
+ num_experts: int = 1,
30
+ experts_per_token: int = 1,
31
+ hidden_dim: int = 1,
32
+ intermediate_size_per_partition: int = 1,
33
+ in_dtype: torch.dtype = torch.bfloat16,
34
+) -> FusedMoEConfig:
35
+ """
36
+ This is a dummy config for the mk constructor interface
37
+ as most kernels like DeepGEMM, CUTLASSFp4, Triton, MARLIN
38
+ do not actually use this config.
39
+
40
+ CUTLASSFp8 needs to set some params for workshapes.
41
+ """
42
+ return FusedMoEConfig(
43
+ num_experts=num_experts,
44
+ experts_per_token=experts_per_token,
45
+ hidden_dim=hidden_dim,
46
+ intermediate_size_per_partition=intermediate_size_per_partition,
47
+ num_local_experts=num_experts,
48
+ moe_parallel_config=FusedMoEParallelConfig.make_no_parallel(),
49
+ activation="silu",
50
+ in_dtype=in_dtype,
51
+ device="cuda",
52
+ routing_method=RoutingMethodType.TopK,
53
+ )
54
+
55
+
56
def triton_moe(
57
a: torch.Tensor,
58
w1: torch.Tensor,
@@ -81,6 +114,7 @@ def batched_moe(
114
max_num_tokens=max_num_tokens,
115
num_dispatchers=1,
116
quant_config=quant_config,
117
+ moe_config=make_dummy_moe_config(),
118
),
119
)
120
@@ -121,6 +155,7 @@ def naive_batched_moe(
155
max_num_tokens=max_num_tokens,
156
num_dispatchers=1,
157
quant_config=quant_config,
158
+ moe_config=make_dummy_moe_config(),
159
),
160
)
161
tools/vllm-rocm/pin_rocm_dependencies.py
+2
-1
@@ -11,10 +11,11 @@ This ensures that 'pip install vllm' automatically installs the correct custom w
11
instead of allowing pip to download different versions from PyPI.
12
"""
13
14
-import re
14
import sys
15
from pathlib import Path
16
17
+import regex as re
18
+
19
20
def extract_version_from_wheel(wheel_name: str) -> str:
21
"""
vllm/compilation/activation_quant_fusion.py
+3
-3
@@ -18,7 +18,7 @@ from vllm.logger import init_logger
18
from vllm.model_executor.layers.quantization.utils.quant_utils import (
19
QuantKey,
20
kFp8StaticTensorSym,
21
- kNvfp4Quant,
21
+ kNvfp4Dynamic,
22
)
23
from vllm.platforms import current_platform
24
@@ -41,7 +41,7 @@ silu_and_mul_nvfp4_quant_supported = current_platform.is_cuda() and hasattr(
41
torch.ops._C, "silu_and_mul_nvfp4_quant"
42
)
43
if silu_and_mul_nvfp4_quant_supported:
44
- FUSED_OPS[kNvfp4Quant] = torch.ops._C.silu_and_mul_nvfp4_quant.default # noqa: E501
44
+ FUSED_OPS[kNvfp4Dynamic] = torch.ops._C.silu_and_mul_nvfp4_quant.default # noqa: E501
45
46
47
class ActivationQuantPattern(ABC):
@@ -129,7 +129,7 @@ class SiluMulNvfp4QuantPattern(ActivationQuantPattern):
129
"""
130
131
def __init__(self) -> None:
132
- super().__init__(kNvfp4Quant)
132
+ super().__init__(kNvfp4Dynamic)
133
134
def get_inputs(self) -> list[torch.Tensor]:
135
result = self.empty_quant(5, 32)
vllm/compilation/fusion.py
+2
-2
@@ -20,7 +20,7 @@ from vllm.model_executor.layers.quantization.utils.quant_utils import (
20
kFp8DynamicTensorSym,
21
kFp8DynamicTokenSym,
22
kFp8StaticTensorSym,
23
- kNvfp4Quant,
23
+ kNvfp4Dynamic,
24
kStaticTensorScale,
25
)
26
from vllm.platforms import current_platform
@@ -63,7 +63,7 @@ QUANT_OPS: dict[QuantKey, OpOverload] = {
63
kFp8DynamicTokenSym: torch.ops._C.dynamic_per_token_scaled_fp8_quant.default, # noqa: E501
64
}
65
if current_platform.is_cuda() and hasattr(torch.ops._C, "scaled_fp4_quant"):
66
- QUANT_OPS[kNvfp4Quant] = torch.ops._C.scaled_fp4_quant.default
66
+ QUANT_OPS[kNvfp4Dynamic] = torch.ops._C.scaled_fp4_quant.default
67
if current_platform.is_cuda():
68
QUANT_OPS[kFp8Dynamic128Sym] = torch.ops._C.per_token_group_fp8_quant.default # noqa: E501
69
QUANT_OPS[kFp8Dynamic64Sym] = torch.ops._C.per_token_group_fp8_quant.default # noqa: E501
vllm/compilation/fusion_attn.py
+2
-2
@@ -16,7 +16,7 @@ from vllm.config import VllmConfig, get_layers_from_vllm_config
16
from vllm.logger import init_logger
17
from vllm.model_executor.layers.quantization.utils.quant_utils import (
18
QuantKey,
19
- kNvfp4Quant,
19
+ kNvfp4Dynamic,
20
kStaticTensorScale,
21
)
22
from vllm.platforms import current_platform
@@ -217,7 +217,7 @@ class AttentionNvfp4QuantPattern(AttentionQuantPattern):
217
"""
218
219
def __init__(self, layer: Attention, dtype: torch.dtype) -> None:
220
- super().__init__(layer, kNvfp4Quant, dtype)
220
+ super().__init__(layer, kNvfp4Dynamic, dtype)
221
222
def _register(self, pm_pass: PatternMatcherPass) -> None:
223
def pattern(
vllm/compilation/matcher_utils.py
+2
-2
@@ -21,7 +21,7 @@ from vllm.model_executor.layers.quantization.utils.quant_utils import (
21
kFp8DynamicTensorSym,
22
kFp8DynamicTokenSym,
23
kFp8StaticTensorSym,
24
- kNvfp4Quant,
24
+ kNvfp4Dynamic,
25
)
26
from vllm.model_executor.layers.rotary_embedding import RotaryEmbedding
27
from vllm.platforms import current_platform
@@ -38,7 +38,7 @@ QUANT_OPS: dict[QuantKey, OpOverload] = {
38
}
39
40
if current_platform.is_cuda() and hasattr(torch.ops._C, "scaled_fp4_quant"):
41
- QUANT_OPS[kNvfp4Quant] = torch.ops._C.scaled_fp4_quant.default # noqa: E501
41
+ QUANT_OPS[kNvfp4Dynamic] = torch.ops._C.scaled_fp4_quant.default # noqa: E501
42
43
if current_platform.is_cuda():
44
QUANT_OPS[kFp8Dynamic128Sym] = torch.ops._C.per_token_group_fp8_quant.default # noqa: E501
vllm/model_executor/layers/fused_moe/batched_deep_gemm_moe.py
+48
-13
@@ -7,11 +7,20 @@ import torch
7
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8
from vllm.forward_context import get_forward_context, is_forward_context_available
9
from vllm.logger import init_logger
10
-from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
10
+from vllm.model_executor.layers.fused_moe.config import (
11
+ FusedMoEConfig,
12
+ FusedMoEParallelConfig,
13
+ FusedMoEQuantConfig,
14
+)
15
from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
16
TopKWeightAndReduceDelegate,
17
)
18
from vllm.model_executor.layers.fused_moe.utils import _resize_cache
19
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
20
+ QuantKey,
21
+ kFp8Dynamic128Sym,
22
+ kFp8Static128BlockSym,
23
+)
24
from vllm.platforms import current_platform
25
from vllm.triton_utils import tl, triton
26
from vllm.utils.deep_gemm import (
@@ -19,6 +28,7 @@ from vllm.utils.deep_gemm import (
28
fp8_m_grouped_gemm_nt_masked,
29
get_mk_alignment_for_contiguous_layout,
30
is_deep_gemm_e8m0_used,
31
+ is_deep_gemm_supported,
32
)
33
from vllm.utils.math_utils import cdiv, round_up
34
@@ -253,29 +263,52 @@ def persistent_masked_m_silu_mul_quant(
263
class BatchedDeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
264
def __init__(
265
self,
266
+ moe_config: FusedMoEConfig,
267
+ quant_config: FusedMoEQuantConfig,
268
max_num_tokens: int,
269
num_dispatchers: int,
258
- quant_config: FusedMoEQuantConfig,
270
):
271
"""
272
max_num_tokens: Maximum number of tokens from a DP Rank
273
num_dispatchers: The number of DP dispatchers.
274
quant_config: Quantization configuration
275
"""
265
- super().__init__(quant_config)
276
+ super().__init__(
277
+ moe_config=moe_config,
278
+ quant_config=quant_config,
279
+ max_num_tokens=max_num_tokens,
280
+ num_dispatchers=num_dispatchers,
281
+ )
282
assert self.block_shape == get_mk_alignment_for_contiguous_layout()
283
assert self.quant_config.use_fp8_w8a8
268
- self.max_num_tokens = max_num_tokens
269
- self.num_dispatchers = num_dispatchers
284
271
- @property
272
- def activation_formats(
273
- self,
274
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
275
- return (
276
- mk.FusedMoEActivationFormat.BatchedExperts,
277
- mk.FusedMoEActivationFormat.BatchedExperts,
278
- )
285
+ @staticmethod
286
+ def activation_format() -> mk.FusedMoEActivationFormat:
287
+ return mk.FusedMoEActivationFormat.BatchedExperts
288
+
289
+ @staticmethod
290
+ def _supports_current_device() -> bool:
291
+ return is_deep_gemm_supported()
292
+
293
+ @staticmethod
294
+ def _supports_no_act_and_mul() -> bool:
295
+ return False
296
+
297
+ @staticmethod
298
+ def _supports_quant_scheme(
299
+ weight_key: QuantKey | None,
300
+ activation_key: QuantKey | None,
301
+ ) -> bool:
302
+ SUPPORTED_W_A = [(kFp8Static128BlockSym, kFp8Dynamic128Sym)]
303
+ return (weight_key, activation_key) in SUPPORTED_W_A
304
+
305
+ @staticmethod
306
+ def _supports_activation(activation: str) -> bool:
307
+ return activation in ["silu"]
308
+
309
+ @staticmethod
310
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
311
+ return True
312
313
def supports_chunking(self) -> bool:
314
return False
@@ -310,6 +343,8 @@ class BatchedDeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
343
# FIXME (varun): We should be able to dispatch only from the leader
344
# DP ranks in the case of TP > 1. At the moment, all the Ranks
345
# end up sending their tokens. This needs to be fixed.
346
+ assert self.num_dispatchers is not None
347
+ assert self.max_num_tokens is not None
348
num_dispatchers = self.num_dispatchers
349
num_experts = local_num_experts
350
max_num_tokens = M if self.max_num_tokens is None else self.max_num_tokens
vllm/model_executor/layers/fused_moe/config.py
+34
-1
@@ -862,6 +862,7 @@ class FusedMoEParallelConfig:
862
863
use_ep: bool # whether to use EP or not
864
all2all_backend: str # all2all backend for MoE communication
865
+ enable_eplb: bool # whether to enable expert load balancing
866
867
@property
868
def use_all2all_kernels(self):
@@ -882,6 +883,16 @@ class FusedMoEParallelConfig:
883
def use_deepep_ll_kernels(self):
884
return self.use_all2all_kernels and self.all2all_backend == "deepep_low_latency"
885
886
+ @property
887
+ def use_batched_activation_format(self):
888
+ return self.use_deepep_ll_kernels or self.use_pplx_kernels
889
+
890
+ @property
891
+ def use_naive_all2all_kernels(self):
892
+ return self.use_all2all_kernels and (
893
+ self.all2all_backend in ["naive", "allgather_reducescatter"]
894
+ )
895
+
896
@staticmethod
897
def flatten_tp_across_dp_and_pcp(
898
tp_size: int, dp_size: int, dp_rank: int, pcp_size: int, pcp_rank: int
@@ -999,6 +1010,7 @@ class FusedMoEParallelConfig:
1010
ep_rank=0,
1011
use_ep=False,
1012
all2all_backend=vllm_parallel_config.all2all_backend,
1013
+ enable_eplb=vllm_parallel_config.enable_eplb,
1014
)
1015
# DP + EP / TP + EP / DP + TP + EP
1016
assert use_ep
@@ -1017,6 +1029,24 @@ class FusedMoEParallelConfig:
1029
ep_rank=ep_rank,
1030
use_ep=True,
1031
all2all_backend=vllm_parallel_config.all2all_backend,
1032
+ enable_eplb=vllm_parallel_config.enable_eplb,
1033
+ )
1034
+
1035
+ @classmethod
1036
+ def make_no_parallel(cls) -> "FusedMoEParallelConfig":
1037
+ """For usage in CI/CD and testing."""
1038
+ return FusedMoEParallelConfig(
1039
+ tp_size=1,
1040
+ tp_rank=0,
1041
+ pcp_size=1,
1042
+ pcp_rank=0,
1043
+ dp_size=1,
1044
+ dp_rank=0,
1045
+ ep_size=1,
1046
+ ep_rank=0,
1047
+ use_ep=False,
1048
+ all2all_backend="naive",
1049
+ enable_eplb=False,
1050
)
1051
1052
@@ -1026,8 +1056,11 @@ class FusedMoEConfig:
1056
num_experts: int
1057
experts_per_token: int
1058
hidden_dim: int
1029
-
1059
+ intermediate_size_per_partition: int
1060
num_local_experts: int
1061
+ activation: str
1062
+ device: torch.device | str
1063
+ routing_method: RoutingMethodType
1064
moe_parallel_config: FusedMoEParallelConfig
1065
1066
# The activation type.
vllm/model_executor/layers/fused_moe/cutlass_moe.py
+164
-91
@@ -7,7 +7,11 @@ import torch
7
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8
from vllm import _custom_ops as ops
9
from vllm.logger import init_logger
10
-from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
10
+from vllm.model_executor.layers.fused_moe.config import (
11
+ FusedMoEConfig,
12
+ FusedMoEParallelConfig,
13
+ FusedMoEQuantConfig,
14
+)
15
from vllm.model_executor.layers.fused_moe.moe_permute_unpermute import (
16
moe_permute,
17
moe_unpermute,
@@ -23,6 +27,19 @@ from vllm.model_executor.layers.fused_moe.utils import (
27
_resize_cache,
28
apply_moe_activation,
29
)
30
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
31
+ QuantKey,
32
+ kFp8DynamicTensorSym,
33
+ kFp8DynamicTokenSym,
34
+ kFp8StaticChannelSym,
35
+ kFp8StaticTensorSym,
36
+ kNvfp4Dynamic,
37
+ kNvfp4Static,
38
+)
39
+from vllm.model_executor.layers.quantization.utils.w8a8_utils import (
40
+ cutlass_group_gemm_supported,
41
+)
42
+from vllm.platforms import current_platform
43
from vllm.scalar_type import scalar_types
44
45
logger = init_logger(__name__)
@@ -238,29 +255,57 @@ def run_cutlass_moe_fp8(
255
class CutlassExpertsFp8Base(mk.FusedMoEPermuteExpertsUnpermute):
256
def __init__(
257
self,
241
- e: int,
242
- n: int,
243
- k: int,
244
- out_dtype: torch.dtype | None,
258
+ moe_config: FusedMoEConfig,
259
quant_config: FusedMoEQuantConfig,
246
- device: torch.dtype,
260
+ max_num_tokens: int | None = None,
261
+ num_dispatchers: int | None = None,
262
):
263
+ super().__init__(
264
+ moe_config=moe_config,
265
+ quant_config=quant_config,
266
+ max_num_tokens=max_num_tokens,
267
+ num_dispatchers=num_dispatchers,
268
+ )
269
assert quant_config.use_fp8_w8a8
249
- super().__init__(quant_config)
270
251
- # E: num_experts
252
- # N: intermediate size per partition
253
- # K: hidden dim
271
+ e = moe_config.num_local_experts
272
+ n = moe_config.intermediate_size_per_partition
273
+ k = moe_config.hidden_dim
274
+ device = moe_config.device
275
ab_strides1_c_strides2 = torch.full((e,), k, device=device, dtype=torch.int64)
276
ab_strides2 = torch.full((e,), n, device=device, dtype=torch.int64)
277
c_strides1 = torch.full((e,), 2 * n, device=device, dtype=torch.int64)
278
258
- self.out_dtype = out_dtype
279
+ self.out_dtype = moe_config.in_dtype
280
self.ab_strides1 = ab_strides1_c_strides2
281
self.ab_strides2 = ab_strides2
282
self.c_strides1 = c_strides1
283
self.c_strides2 = ab_strides1_c_strides2
284
285
+ @staticmethod
286
+ def _supports_current_device() -> bool:
287
+ return cutlass_group_gemm_supported()
288
+
289
+ @staticmethod
290
+ def _supports_no_act_and_mul() -> bool:
291
+ return False
292
+
293
+ @staticmethod
294
+ def _supports_quant_scheme(
295
+ weight_key: QuantKey | None,
296
+ activation_key: QuantKey | None,
297
+ ) -> bool:
298
+ SUPPORTED_W_A = [
299
+ (kFp8StaticChannelSym, kFp8DynamicTokenSym),
300
+ (kFp8StaticTensorSym, kFp8DynamicTensorSym),
301
+ (kFp8StaticTensorSym, kFp8StaticTensorSym),
302
+ ]
303
+ return (weight_key, activation_key) in SUPPORTED_W_A
304
+
305
+ @staticmethod
306
+ def _supports_activation(activation: str) -> bool:
307
+ return activation in ["silu", "gelu", "swigluoai"]
308
+
309
def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
310
# Let PrepareAndFinalize::finalize() decide the impl.
311
return TopKWeightAndReduceDelegate()
@@ -291,7 +336,7 @@ class CutlassExpertsFp8Base(mk.FusedMoEPermuteExpertsUnpermute):
336
expert_num_tokens = expert_tokens_meta.expert_num_tokens
337
338
use_batched_format = (
294
- self.activation_formats[0] == mk.FusedMoEActivationFormat.BatchedExperts
339
+ self.activation_format() == mk.FusedMoEActivationFormat.BatchedExperts
340
)
341
342
in_dtype = hidden_states.dtype
@@ -324,20 +369,23 @@ class CutlassExpertsFp8Base(mk.FusedMoEPermuteExpertsUnpermute):
369
370
371
class CutlassExpertsFp8(CutlassExpertsFp8Base):
327
- @property
328
- def activation_formats(
329
- self,
330
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
331
- return (
332
- mk.FusedMoEActivationFormat.Standard,
333
- mk.FusedMoEActivationFormat.Standard,
334
- )
372
+ @staticmethod
373
+ def activation_format() -> mk.FusedMoEActivationFormat:
374
+ return mk.FusedMoEActivationFormat.Standard
375
+
376
+ @staticmethod
377
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
378
+ # CutlassExpertsFp8 does not support expert map, which is
379
+ # needed for STANDARD activation format kernels in DP/EP mode.
380
+ # Note that the BATCHED activation format does not use
381
+ # the expert map for identifying experts.
382
+ return not moe_parallel_config.use_all2all_kernels
383
384
def supports_chunking(self) -> bool:
385
return True
386
387
def supports_expert_map(self) -> bool:
340
- return True
388
+ return False
389
390
def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
391
# topk weights and reduction are fused in moe_unpermute cuda kernel
@@ -365,26 +413,16 @@ class CutlassExpertsFp8(CutlassExpertsFp8Base):
413
414
415
class CutlassBatchedExpertsFp8(CutlassExpertsFp8Base):
368
- def __init__(
369
- self,
370
- max_experts_per_worker: int,
371
- num_dispatchers: int,
372
- *args,
373
- **kwargs,
374
- ):
375
- super().__init__(*args, **kwargs)
376
- assert max_experts_per_worker > 0
377
- self.max_experts_per_worker = max_experts_per_worker
378
- self.num_dispatchers = num_dispatchers
416
+ @staticmethod
417
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
418
+ # BATCHED activation format works with EP because
419
+ # expert_map is not used to identify experts (the
420
+ # info is encoded/managed by the P/F logic).
421
+ return True
422
380
- @property
381
- def activation_formats(
382
- self,
383
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
384
- return (
385
- mk.FusedMoEActivationFormat.BatchedExperts,
386
- mk.FusedMoEActivationFormat.BatchedExperts,
387
- )
423
+ @staticmethod
424
+ def activation_format() -> mk.FusedMoEActivationFormat:
425
+ return mk.FusedMoEActivationFormat.BatchedExperts
426
427
def supports_chunking(self) -> bool:
428
return False
@@ -408,14 +446,15 @@ class CutlassBatchedExpertsFp8(CutlassExpertsFp8Base):
446
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
447
num_dp = self.num_dispatchers
448
assert num_dp is not None
449
+ experts_per_worker = self.moe_config.num_local_experts
450
activation_out_dim = self.adjust_N_for_activation(N, activation)
412
- workspace1 = (self.max_experts_per_worker, M * num_dp, max(N, K))
451
+ workspace1 = (experts_per_worker, M * num_dp, max(N, K))
452
workspace2 = (
414
- self.max_experts_per_worker,
453
+ experts_per_worker,
454
M * num_dp,
455
max(activation_out_dim, K),
456
)
418
- output = (self.max_experts_per_worker, M, K)
457
+ output = (experts_per_worker, M, K)
458
return (workspace1, workspace2, output)
459
460
@@ -601,34 +640,41 @@ def run_cutlass_moe_fp4(
640
return
641
642
604
-# Split into batched and non-batched
643
class CutlassExpertsFp4(mk.FusedMoEPermuteExpertsUnpermute):
606
- def __init__(
607
- self,
608
- max_experts_per_worker: int,
609
- out_dtype: torch.dtype,
610
- quant_config: FusedMoEQuantConfig,
611
- use_batched_format: bool = False,
612
- ):
613
- super().__init__(quant_config)
614
- self.max_experts_per_worker = max_experts_per_worker
615
- self.out_dtype = out_dtype
616
- self.use_batched_format = use_batched_format
644
+ @staticmethod
645
+ def expects_unquantized_inputs(
646
+ moe_config: FusedMoEConfig, quant_config: FusedMoEQuantConfig
647
+ ) -> bool:
648
+ return True
649
618
- @property
619
- def activation_formats(
620
- self,
621
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
622
- if self.use_batched_format:
623
- return (
624
- mk.FusedMoEActivationFormat.BatchedExperts,
625
- mk.FusedMoEActivationFormat.BatchedExperts,
626
- )
627
- else:
628
- return (
629
- mk.FusedMoEActivationFormat.Standard,
630
- mk.FusedMoEActivationFormat.Standard,
631
- )
650
+ @staticmethod
651
+ def _supports_current_device() -> bool:
652
+ return current_platform.has_device_capability((10, 0))
653
+
654
+ @staticmethod
655
+ def _supports_no_act_and_mul() -> bool:
656
+ return False
657
+
658
+ @staticmethod
659
+ def _supports_quant_scheme(
660
+ weight_key: QuantKey | None,
661
+ activation_key: QuantKey | None,
662
+ ) -> bool:
663
+ return (weight_key, activation_key) == (kNvfp4Static, kNvfp4Dynamic)
664
+
665
+ @staticmethod
666
+ def _supports_activation(activation: str) -> bool:
667
+ return activation in ["silu", "gelu", "swigluoai"]
668
+
669
+ @staticmethod
670
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
671
+ # CutlassExpertsFp4 does not support expert map, which is
672
+ # needed for STANDARD activation format kernels in EP mode.
673
+ return moe_parallel_config.ep_size == 1
674
+
675
+ @staticmethod
676
+ def activation_format() -> mk.FusedMoEActivationFormat:
677
+ return mk.FusedMoEActivationFormat.Standard
678
679
def supports_expert_map(self) -> bool:
680
return False
@@ -640,7 +686,7 @@ class CutlassExpertsFp4(mk.FusedMoEPermuteExpertsUnpermute):
686
return TopKWeightAndReduceNoOP()
687
688
def workspace_dtype(self, act_dtype: torch.dtype) -> torch.dtype:
643
- return self.out_dtype if self.out_dtype is not None else act_dtype
689
+ return act_dtype
690
691
def workspace_shapes(
692
self,
@@ -653,18 +699,9 @@ class CutlassExpertsFp4(mk.FusedMoEPermuteExpertsUnpermute):
699
expert_tokens_meta: mk.ExpertTokensMetadata | None,
700
activation: str,
701
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
656
- activation_out_dim = self.adjust_N_for_activation(N, activation)
657
- workspace1: tuple[int, ...] = ()
658
- workspace2: tuple[int, ...] = ()
659
- output: tuple[int, ...] = ()
660
- if self.use_batched_format:
661
- workspace1 = (self.max_experts_per_worker, M, max(N, K))
662
- workspace2 = (self.max_experts_per_worker, M, activation_out_dim)
663
- output = (self.max_experts_per_worker, M, K)
664
- else:
665
- workspace1 = (M * topk, max(2 * N, K))
666
- workspace2 = (M * topk, N)
667
- output = (M, K)
702
+ workspace1 = (M * topk, max(2 * N, K))
703
+ workspace2 = (M * topk, N)
704
+ output = (M, K)
705
return (workspace1, workspace2, output)
706
707
def apply(
@@ -869,10 +906,11 @@ class CutlassExpertsW4A8Fp8(mk.FusedMoEPermuteExpertsUnpermute):
906
c_strides2: torch.Tensor,
907
s_strides1: torch.Tensor,
908
s_strides2: torch.Tensor,
909
+ moe_config: FusedMoEConfig,
910
quant_config: FusedMoEQuantConfig,
911
group_size: int,
912
):
875
- super().__init__(quant_config)
913
+ super().__init__(moe_config=moe_config, quant_config=quant_config)
914
self.out_dtype = out_dtype
915
self.a_strides1 = a_strides1
916
self.a_strides2 = a_strides2
@@ -884,13 +922,46 @@ class CutlassExpertsW4A8Fp8(mk.FusedMoEPermuteExpertsUnpermute):
922
self.s_strides2 = s_strides2
923
self.group_size = group_size
924
887
- @property
888
- def activation_formats(
889
- self,
890
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
891
- return (
892
- mk.FusedMoEActivationFormat.Standard,
893
- mk.FusedMoEActivationFormat.Standard,
925
+ @staticmethod
926
+ def activation_format() -> mk.FusedMoEActivationFormat:
927
+ return mk.FusedMoEActivationFormat.Standard
928
+
929
+ @staticmethod
930
+ def _supports_current_device() -> bool:
931
+ raise NotImplementedError(
932
+ "CutlassExpertsW4A8Fp8 is not yet used by an Oracle. "
933
+ "This method should not be called."
934
+ )
935
+
936
+ @staticmethod
937
+ def _supports_no_act_and_mul() -> bool:
938
+ raise NotImplementedError(
939
+ "CutlassExpertsW4A8Fp8 is not yet used by an Oracle. "
940
+ "This method should not be called."
941
+ )
942
+
943
+ @staticmethod
944
+ def _supports_quant_scheme(
945
+ weight_key: QuantKey | None,
946
+ activation_key: QuantKey | None,
947
+ ) -> bool:
948
+ raise NotImplementedError(
949
+ "CutlassExpertsW4A8Fp8 is not yet used by an Oracle. "
950
+ "This method should not be called."
951
+ )
952
+
953
+ @staticmethod
954
+ def _supports_activation(activation: str) -> bool:
955
+ raise NotImplementedError(
956
+ "CutlassExpertsW4A8Fp8 is not yet used by an Oracle. "
957
+ "This method should not be called."
958
+ )
959
+
960
+ @staticmethod
961
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
962
+ raise NotImplementedError(
963
+ "CutlassExpertsW4A8Fp8 is not yet used by an Oracle. "
964
+ "This method should not be called."
965
)
966
967
def supports_chunking(self) -> bool:
@@ -947,7 +1018,7 @@ class CutlassExpertsW4A8Fp8(mk.FusedMoEPermuteExpertsUnpermute):
1018
expert_num_tokens = None
1019
1020
use_batched_format = (
950
- self.activation_formats[0] == mk.FusedMoEActivationFormat.BatchedExperts
1021
+ self.activation_format() == mk.FusedMoEActivationFormat.BatchedExperts
1022
)
1023
assert not use_batched_format, "batched format not supported"
1024
@@ -1003,6 +1074,7 @@ def cutlass_moe_w4a8_fp8(
1074
s_strides1: torch.Tensor,
1075
s_strides2: torch.Tensor,
1076
quant_config: FusedMoEQuantConfig,
1077
+ moe_config: FusedMoEConfig,
1078
activation: str = "silu",
1079
expert_map: torch.Tensor | None = None,
1080
apply_router_weight_on_input: bool = False,
@@ -1076,6 +1148,7 @@ def cutlass_moe_w4a8_fp8(
1148
c_strides2=c_strides2,
1149
s_strides1=s_strides1,
1150
s_strides2=s_strides2,
1151
+ moe_config=moe_config,
1152
quant_config=quant_config,
1153
group_size=group_size,
1154
),
vllm/model_executor/layers/fused_moe/deep_gemm_moe.py
+39
-93
@@ -6,17 +6,15 @@ import torch
6
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
from vllm.logger import init_logger
8
from vllm.model_executor.layers.fused_moe.config import (
9
+ FusedMoEConfig,
10
+ FusedMoEParallelConfig,
11
FusedMoEQuantConfig,
10
- fp8_w8a8_moe_quant_config,
12
)
13
from vllm.model_executor.layers.fused_moe.deep_gemm_utils import (
14
compute_aligned_M,
15
deepgemm_moe_permute,
16
deepgemm_unpermute_and_reduce,
17
)
17
-from vllm.model_executor.layers.fused_moe.prepare_finalize import (
18
- MoEPrepareAndFinalizeNoEP,
19
-)
18
from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
19
TopKWeightAndReduceNoOP,
20
)
@@ -26,9 +24,15 @@ from vllm.model_executor.layers.quantization.utils.fp8_utils import (
24
per_token_group_quant_fp8_packed_for_deepgemm,
25
silu_mul_per_token_group_quant_fp8_colmajor,
26
)
27
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
28
+ QuantKey,
29
+ kFp8Dynamic128Sym,
30
+ kFp8Static128BlockSym,
31
+)
32
from vllm.utils.deep_gemm import (
33
DeepGemmQuantScaleFMT,
34
get_mk_alignment_for_contiguous_layout,
35
+ is_deep_gemm_supported,
36
m_grouped_fp8_gemm_nt_contiguous,
37
)
38
from vllm.utils.import_utils import has_deep_gemm
@@ -109,21 +113,42 @@ def _valid_deep_gemm(
113
114
115
class DeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
112
- def __init__(self, quant_config: FusedMoEQuantConfig):
113
- super().__init__(quant_config)
116
+ def __init__(self, moe_config: FusedMoEConfig, quant_config: FusedMoEQuantConfig):
117
+ super().__init__(moe_config=moe_config, quant_config=quant_config)
118
assert quant_config.block_shape == get_mk_alignment_for_contiguous_layout()
119
assert quant_config.quant_dtype == torch.float8_e4m3fn
120
assert not quant_config.per_act_token_quant
121
assert not quant_config.per_out_ch_quant
122
119
- @property
120
- def activation_formats(
121
- self,
122
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
123
- return (
124
- mk.FusedMoEActivationFormat.Standard,
125
- mk.FusedMoEActivationFormat.Standard,
126
- )
123
+ @staticmethod
124
+ def activation_format() -> mk.FusedMoEActivationFormat:
125
+ return mk.FusedMoEActivationFormat.Standard
126
+
127
+ @staticmethod
128
+ def _supports_current_device() -> bool:
129
+ return is_deep_gemm_supported()
130
+
131
+ @staticmethod
132
+ def _supports_no_act_and_mul() -> bool:
133
+ return False
134
+
135
+ @staticmethod
136
+ def _supports_quant_scheme(
137
+ weight_key: QuantKey | None,
138
+ activation_key: QuantKey | None,
139
+ ) -> bool:
140
+ SUPPORTED_W_A = [
141
+ (kFp8Static128BlockSym, kFp8Dynamic128Sym),
142
+ ]
143
+ return (weight_key, activation_key) in SUPPORTED_W_A
144
+
145
+ @staticmethod
146
+ def _supports_activation(activation: str) -> bool:
147
+ return activation in ["silu"]
148
+
149
+ @staticmethod
150
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
151
+ return True
152
153
def supports_chunking(self) -> bool:
154
return True
@@ -283,82 +308,3 @@ class DeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
308
expert_map=expert_map,
309
output=output,
310
)
286
-
287
-
288
-def deep_gemm_moe_fp8(
289
- hidden_states: torch.Tensor,
290
- w1: torch.Tensor,
291
- w2: torch.Tensor,
292
- w1_scale: torch.Tensor,
293
- w2_scale: torch.Tensor,
294
- topk_weights: torch.Tensor,
295
- topk_ids: torch.Tensor,
296
- inplace: bool = False,
297
- activation: str = "silu",
298
- global_num_experts: int = -1,
299
- expert_map: torch.Tensor | None = None,
300
- a1_scale: torch.Tensor | None = None,
301
- a2_scale: torch.Tensor | None = None,
302
- apply_router_weight_on_input: bool = False,
303
-) -> torch.Tensor:
304
- """
305
- This function computes a a8w8-quantized Mixture of Experts (MoE) layer
306
- using two sets of quantized weights, w1_q and w2_q, and top-k gating
307
- mechanism. The matrix multiplications are implemented with DeepGemm
308
- grouped gemm.
309
-
310
- Parameters:
311
- - hidden_states (torch.Tensor): The input tensor to the MoE layer.
312
- Shape: [M, K]
313
- - w1 (torch.Tensor): The first set of fp8 quantized expert weights.
314
- Shape: [num_experts, K, 2N] (the weights are passed transposed)
315
- - w2 (torch.Tensor): The second set of fp8 quantized expert weights.
316
- Shape: [num_experts, N, K] (the weights are passed transposed)
317
- - w1_scale (torch.Tensor): The fp32 scale to dequantize w1_q.
318
- Shape: [num_experts] or [num_experts, 2N]
319
- - w2_scale (torch.Tensor): The fp32 scale to dequantize w2_q.
320
- Shape: [num_experts] or [num_experts, K]
321
- - topk_weights (torch.Tensor): The weights of each token->expert mapping.
322
- - topk_ids (torch.Tensor): The token->expert mapping for topk_weights.
323
- - inplace (bool): If True, perform the operation in-place.
324
- Defaults to False.
325
- - activation (str): The activation function to apply after the first
326
- MoE layer.
327
- - global_num_experts (int): The total number of experts in the global
328
- expert space.
329
- - expert_map (Optional[torch.Tensor]): A tensor mapping expert indices
330
- from the global expert space to the local expert space of the expert
331
- parallel shard.
332
- - a1_scale (Optional[torch.Tensor]): The optional fp32 scale to quantize a.
333
- Shape: scalar or [M]
334
- - a2_scale (Optional[torch.Tensor]): The optional fp32 scale to
335
- quantize the intermediate result between the gemms.
336
- Shape: scalar or [M]
337
-
338
- Returns:
339
- - torch.Tensor: The bfloat16 output tensor after applying the MoE layer.
340
- """
341
- quant_config = fp8_w8a8_moe_quant_config(
342
- w1_scale=w1_scale,
343
- w2_scale=w2_scale,
344
- a1_scale=a1_scale,
345
- a2_scale=a2_scale,
346
- block_shape=get_mk_alignment_for_contiguous_layout(),
347
- )
348
-
349
- fn = mk.FusedMoEModularKernel(
350
- MoEPrepareAndFinalizeNoEP(),
351
- DeepGemmExperts(quant_config),
352
- )
353
- return fn(
354
- hidden_states,
355
- w1,
356
- w2,
357
- topk_weights,
358
- topk_ids,
359
- inplace=inplace,
360
- activation=activation,
361
- global_num_experts=global_num_experts,
362
- expert_map=expert_map,
363
- apply_router_weight_on_input=apply_router_weight_on_input,
364
- )
vllm/model_executor/layers/fused_moe/fallback.py
+70
-8
@@ -6,6 +6,8 @@ from abc import ABC, abstractmethod
6
import torch
7
8
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
9
+from vllm.model_executor.layers.fused_moe.config import FusedMoEParallelConfig
10
+from vllm.model_executor.layers.quantization.utils.quant_utils import QuantKey
11
12
13
class FallbackExperts(mk.FusedMoEPermuteExpertsUnpermute, ABC):
@@ -16,18 +18,78 @@ class FallbackExperts(mk.FusedMoEPermuteExpertsUnpermute, ABC):
18
experts: mk.FusedMoEPermuteExpertsUnpermute,
19
fallback_experts: mk.FusedMoEPermuteExpertsUnpermute,
20
):
19
- super().__init__(experts.quant_config)
21
+ super().__init__(
22
+ moe_config=experts.moe_config, quant_config=experts.quant_config
23
+ )
24
self.fallback_experts = fallback_experts
25
self.experts = experts
26
23
- @property
24
- def activation_formats(
25
- self,
26
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
27
- assert (
28
- self.fallback_experts.activation_formats == self.experts.activation_formats
27
+ @staticmethod
28
+ def get_clses() -> tuple[
29
+ type[mk.FusedMoEPermuteExpertsUnpermute],
30
+ type[mk.FusedMoEPermuteExpertsUnpermute],
31
+ ]:
32
+ """
33
+ Get the cls for the experts and fallback experts.
34
+
35
+ Subclasses should implement this method, so that
36
+ we have a consistent way to call the _supports_*
37
+ class methods below.
38
+ """
39
+ raise NotImplementedError(
40
+ "Subclasses must return the cls for the experts and fallback experts."
41
+ )
42
+
43
+ @classmethod
44
+ def activation_format(
45
+ cls: type["FallbackExperts"],
46
+ ) -> mk.FusedMoEActivationFormat:
47
+ experts_cls, fallback_cls = cls.get_clses()
48
+ assert experts_cls.activation_format() == fallback_cls.activation_format()
49
+ return experts_cls.activation_format()
50
+
51
+ @classmethod
52
+ def _supports_current_device(cls) -> bool:
53
+ experts_cls, fallback_cls = cls.get_clses()
54
+ return (
55
+ experts_cls._supports_current_device()
56
+ and fallback_cls._supports_current_device()
57
+ )
58
+
59
+ @classmethod
60
+ def _supports_no_act_and_mul(cls) -> bool:
61
+ experts_cls, fallback_cls = cls.get_clses()
62
+ return (
63
+ experts_cls._supports_no_act_and_mul()
64
+ and fallback_cls._supports_no_act_and_mul()
65
)
30
- return self.fallback_experts.activation_formats
66
+
67
+ @classmethod
68
+ def _supports_quant_scheme(
69
+ cls,
70
+ weight_key: QuantKey | None,
71
+ activation_key: QuantKey | None,
72
+ ) -> bool:
73
+ experts_cls, fallback_cls = cls.get_clses()
74
+ return experts_cls._supports_quant_scheme(
75
+ weight_key, activation_key
76
+ ) and fallback_cls._supports_quant_scheme(weight_key, activation_key)
77
+
78
+ @classmethod
79
+ def _supports_activation(cls, activation: str) -> bool:
80
+ experts_cls, fallback_cls = cls.get_clses()
81
+ return experts_cls._supports_activation(
82
+ activation
83
+ ) and fallback_cls._supports_activation(activation)
84
+
85
+ @classmethod
86
+ def _supports_parallel_config(
87
+ cls, moe_parallel_config: FusedMoEParallelConfig
88
+ ) -> bool:
89
+ experts_cls, fallback_cls = cls.get_clses()
90
+ return experts_cls._supports_parallel_config(
91
+ moe_parallel_config
92
+ ) and fallback_cls._supports_parallel_config(moe_parallel_config)
93
94
def supports_chunking(self) -> bool:
95
assert (
vllm/model_executor/layers/fused_moe/flashinfer_cutedsl_moe.py
+50
-41
@@ -6,13 +6,22 @@ import torch
6
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
from vllm import envs
8
from vllm.logger import init_logger
9
-from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
9
+from vllm.model_executor.layers.fused_moe.config import (
10
+ FusedMoEConfig,
11
+ FusedMoEParallelConfig,
12
+ FusedMoEQuantConfig,
13
+)
14
from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
15
TopKWeightAndReduceDelegate,
16
)
17
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
18
+ QuantKey,
19
+ kNvfp4Dynamic,
20
+ kNvfp4Static,
21
+)
22
+from vllm.platforms import current_platform
23
from vllm.utils.flashinfer import (
24
flashinfer_cutedsl_grouped_gemm_nt_masked,
15
- has_flashinfer_cutedsl_grouped_gemm_nt_masked,
25
scaled_fp4_grouped_quantize,
26
silu_and_mul_scaled_nvfp4_experts_quantize,
27
)
@@ -20,54 +29,54 @@ from vllm.utils.flashinfer import (
29
logger = init_logger(__name__)
30
31
23
-def is_valid_flashinfer_cutedsl_fused_moe(
24
- hidden_states: torch.Tensor, w1: torch.Tensor, w2: torch.Tensor
25
-) -> bool:
26
- """
27
- Check if the given problem size is supported by the FlashInfer CuteDSL MoE
28
- kernel.
29
- """
30
- if not has_flashinfer_cutedsl_grouped_gemm_nt_masked():
31
- logger.debug_once(
32
- "FlashInferCuteDSLExperts disabled: "
33
- "flashinfer_cutedsl_fused_moe not available."
34
- )
35
- return False
36
- # Data type checks
37
- if (
38
- w1.dtype != torch.uint8
39
- or w2.dtype != torch.uint8
40
- or hidden_states.dtype not in [torch.float32, torch.float16, torch.bfloat16]
41
- ):
42
- logger.debug_once(
43
- "FlashInferCuteDSLExperts disabled: w1/w2 must be torch.uint8 "
44
- f"(got w1={w1.dtype}, w2={w2.dtype}), hidden_states must be "
45
- f"float32, float16, or bfloat16 (got {hidden_states.dtype})."
46
- )
47
- return False
48
- return True
49
-
50
-
32
class FlashInferCuteDSLExperts(mk.FusedMoEPermuteExpertsUnpermute):
33
def __init__(
34
self,
54
- out_dtype: torch.dtype,
35
+ moe_config: FusedMoEConfig,
36
quant_config: FusedMoEQuantConfig,
37
+ max_num_tokens: int,
38
+ num_dispatchers: int,
39
):
57
- super().__init__(quant_config)
40
+ super().__init__(
41
+ moe_config=moe_config,
42
+ quant_config=quant_config,
43
+ max_num_tokens=max_num_tokens,
44
+ num_dispatchers=num_dispatchers,
45
+ )
46
assert quant_config.quant_dtype == "nvfp4", (
47
"Only nvfp4 quantization are currently supported."
48
)
61
- self.out_dtype = out_dtype
49
+ self.out_dtype = moe_config.in_dtype
50
63
- @property
64
- def activation_formats(
65
- self,
66
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
67
- return (
68
- mk.FusedMoEActivationFormat.BatchedExperts,
69
- mk.FusedMoEActivationFormat.BatchedExperts,
70
- )
51
+ @staticmethod
52
+ def activation_format() -> mk.FusedMoEActivationFormat:
53
+ return mk.FusedMoEActivationFormat.BatchedExperts
54
+
55
+ @staticmethod
56
+ def _supports_current_device() -> bool:
57
+ return current_platform.is_device_capability_family(100)
58
+
59
+ @staticmethod
60
+ def _supports_no_act_and_mul() -> bool:
61
+ return False
62
+
63
+ @staticmethod
64
+ def _supports_quant_scheme(
65
+ weight_key: QuantKey | None,
66
+ activation_key: QuantKey | None,
67
+ ) -> bool:
68
+ SUPPORTED_W_A = [
69
+ (kNvfp4Static, kNvfp4Dynamic),
70
+ ]
71
+ return (weight_key, activation_key) in SUPPORTED_W_A
72
+
73
+ @staticmethod
74
+ def _supports_activation(activation: str) -> bool:
75
+ return activation in ["silu"]
76
+
77
+ @staticmethod
78
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
79
+ return True
80
81
def supports_expert_map(self) -> bool:
82
return False
vllm/model_executor/layers/fused_moe/flashinfer_cutlass_moe.py
+93
-106
@@ -5,13 +5,22 @@ import torch
5
6
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
from vllm.logger import init_logger
8
-from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
9
-from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_prepare_finalize import ( # noqa: E501
10
- create_flashinfer_prepare_finalize,
8
+from vllm.model_executor.layers.fused_moe.config import (
9
+ FusedMoEParallelConfig,
10
+ FusedMoEQuantConfig,
11
)
12
from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
13
TopKWeightAndReduceNoOP,
14
)
15
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
16
+ QuantKey,
17
+ kFp8Dynamic128Sym,
18
+ kFp8Static128BlockSym,
19
+ kFp8StaticTensorSym,
20
+ kNvfp4Dynamic,
21
+ kNvfp4Static,
22
+)
23
+from vllm.platforms import current_platform
24
from vllm.utils.flashinfer import (
25
flashinfer_cutlass_fused_moe,
26
has_flashinfer_cutlass_fused_moe,
@@ -50,40 +59,100 @@ def is_valid_flashinfer_cutlass_fused_moe(
59
class FlashInferExperts(mk.FusedMoEPermuteExpertsUnpermute):
60
def __init__(
61
self,
53
- out_dtype: torch.dtype,
62
+ moe_config: mk.FusedMoEConfig,
63
quant_config: FusedMoEQuantConfig,
55
- ep_rank: int = 0,
56
- ep_size: int = 1,
57
- tp_rank: int = 0,
58
- tp_size: int = 1,
59
- use_dp: bool = False,
60
- use_deepseek_fp8_block_scale: bool = False,
64
):
62
- super().__init__(quant_config)
65
+ super().__init__(moe_config, quant_config)
66
assert quant_config.quant_dtype in ("nvfp4", torch.float8_e4m3fn, None), (
67
"Only nvfp4, fp8, bfloat16 and"
68
" float16 quantization are currently supported."
69
)
67
- self.ep_rank = ep_rank
68
- self.ep_size = ep_size
69
- self.tp_rank = tp_rank
70
- self.tp_size = tp_size
71
- self.out_dtype = out_dtype
72
- self.use_dp = use_dp
70
+ self.ep_rank = moe_config.moe_parallel_config.ep_rank
71
+ self.ep_size = moe_config.moe_parallel_config.ep_size
72
+ self.tp_rank = moe_config.moe_parallel_config.tp_rank
73
+ self.tp_size = moe_config.moe_parallel_config.tp_size
74
+ self.out_dtype = moe_config.in_dtype
75
+ self.use_dp = moe_config.moe_parallel_config.dp_size > 1
76
# Enables DeepSeek-style FP8 block-scale path:
77
# - pass per-block weight scales to the kernel
78
# - skip input activation quantization (kernel applies scaling)
76
- self.use_deepseek_fp8_block_scale = use_deepseek_fp8_block_scale
79
+ self.use_deepseek_fp8_block_scale = quant_config.is_block_quantized
80
78
- @property
79
- def activation_formats(
80
- self,
81
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
81
+ @staticmethod
82
+ def expects_unquantized_inputs(
83
+ moe_config: mk.FusedMoEConfig, quant_config: FusedMoEQuantConfig
84
+ ) -> bool:
85
+ # NVFP4 TP kernels and FP8 block-quantized kernels apply
86
+ # input quantization inside FusedMoEPermuteExpertsUnpermute.
87
+ return (
88
+ quant_config.use_nvfp4_w4a4
89
+ and not moe_config.moe_parallel_config.use_all2all_kernels
90
+ ) or (quant_config.use_fp8_w8a8 and quant_config.is_block_quantized)
91
+
92
+ @staticmethod
93
+ def _supports_current_device() -> bool:
94
return (
83
- mk.FusedMoEActivationFormat.Standard,
84
- mk.FusedMoEActivationFormat.Standard,
95
+ current_platform.is_cuda()
96
+ and (
97
+ current_platform.is_device_capability((9, 0))
98
+ or current_platform.is_device_capability_family(100)
99
+ )
100
+ and has_flashinfer_cutlass_fused_moe()
101
)
102
103
+ @staticmethod
104
+ def _supports_no_act_and_mul() -> bool:
105
+ return True
106
+
107
+ @staticmethod
108
+ def _supports_quant_scheme(
109
+ weight_key: QuantKey | None,
110
+ activation_key: QuantKey | None,
111
+ ) -> bool:
112
+ # The following are supported by FlashInferExperts:
113
+ # * unquantized
114
+ # * fp8 static per-tensor on 9.0+
115
+ # * fp8 block on 9.0
116
+ # * nvfp4 on 10.0+
117
+
118
+ p = current_platform
119
+ scheme = (weight_key, activation_key)
120
+ return (
121
+ (
122
+ scheme
123
+ in [
124
+ (None, None),
125
+ (kFp8StaticTensorSym, kFp8StaticTensorSym),
126
+ ]
127
+ )
128
+ or (
129
+ (scheme == (kFp8Static128BlockSym, kFp8Dynamic128Sym))
130
+ and (p.is_device_capability((9, 0)))
131
+ )
132
+ or (
133
+ (scheme == (kNvfp4Static, kNvfp4Dynamic))
134
+ and (p.is_device_capability_family(100))
135
+ )
136
+ )
137
+
138
+ @staticmethod
139
+ def _supports_activation(activation: str) -> bool:
140
+ return activation in ["silu", "relu2_no_mul"]
141
+
142
+ @staticmethod
143
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
144
+ # FLASHINFER_CUTLASS currently uses its down P/F, which does not
145
+ # work with SP. This will be removed in follow up after we get
146
+ # rid of the FlashInfer specific P/F function.
147
+ return (
148
+ moe_parallel_config.dp_size == 1
149
+ or moe_parallel_config.dp_size == moe_parallel_config.ep_size
150
+ )
151
+
152
+ @staticmethod
153
+ def activation_format() -> mk.FusedMoEActivationFormat:
154
+ return mk.FusedMoEActivationFormat.Standard
155
+
156
def supports_expert_map(self) -> bool:
157
return False
158
@@ -231,85 +300,3 @@ class FlashInferExperts(mk.FusedMoEPermuteExpertsUnpermute):
300
# No support for LoRA in flashinfer_cutlass_fused_moe.
301
# See TODOs in flashinfer functions runMoe and runMoeMinLantency.
302
raise NotImplementedError("LoRA is not supported for flashinfer_cutlass_moe")
234
-
235
-
236
-def flashinfer_cutlass_moe_fp4(
237
- hidden_states: torch.Tensor,
238
- w1: torch.Tensor,
239
- w2: torch.Tensor,
240
- topk_weights: torch.Tensor,
241
- topk_ids: torch.Tensor,
242
- quant_config: FusedMoEQuantConfig,
243
- inplace: bool = False,
244
- activation: str = "silu",
245
- global_num_experts: int = -1,
246
- expert_map: torch.Tensor | None = None,
247
- apply_router_weight_on_input: bool = False,
248
-) -> torch.Tensor:
249
- fused_experts = mk.FusedMoEModularKernel(
250
- create_flashinfer_prepare_finalize(
251
- use_dp=False, use_nvfp4=True, enable_alltoallv=False
252
- ),
253
- FlashInferExperts(
254
- out_dtype=hidden_states.dtype,
255
- quant_config=quant_config,
256
- use_dp=False,
257
- ),
258
- )
259
-
260
- return fused_experts(
261
- hidden_states=hidden_states,
262
- w1=w1,
263
- w2=w2,
264
- topk_weights=topk_weights,
265
- topk_ids=topk_ids,
266
- inplace=inplace,
267
- activation=activation,
268
- global_num_experts=global_num_experts,
269
- expert_map=expert_map,
270
- apply_router_weight_on_input=apply_router_weight_on_input,
271
- )
272
-
273
-
274
-def flashinfer_cutlass_moe(
275
- hidden_states: torch.Tensor,
276
- w1: torch.Tensor,
277
- w2: torch.Tensor,
278
- topk_weights: torch.Tensor,
279
- topk_ids: torch.Tensor,
280
- quant_config: FusedMoEQuantConfig,
281
- inplace: bool = False,
282
- activation: str = "silu",
283
- global_num_experts: int = -1,
284
- expert_map: torch.Tensor | None = None,
285
- apply_router_weight_on_input: bool = False,
286
- tp_rank: int = 0,
287
- tp_size: int = 1,
288
- ep_rank: int = 0,
289
- ep_size: int = 1,
290
- use_dp: bool = False,
291
-) -> torch.Tensor:
292
- fused_experts = mk.FusedMoEModularKernel(
293
- create_flashinfer_prepare_finalize(use_dp=use_dp),
294
- FlashInferExperts(
295
- out_dtype=hidden_states.dtype,
296
- quant_config=quant_config,
297
- tp_rank=tp_rank,
298
- tp_size=tp_size,
299
- ep_rank=ep_rank,
300
- ep_size=ep_size,
301
- ),
302
- )
303
-
304
- return fused_experts(
305
- hidden_states=hidden_states,
306
- w1=w1,
307
- w2=w2,
308
- topk_weights=topk_weights,
309
- topk_ids=topk_ids,
310
- inplace=inplace,
311
- activation=activation,
312
- global_num_experts=global_num_experts,
313
- expert_map=expert_map,
314
- apply_router_weight_on_input=apply_router_weight_on_input,
315
- )
vllm/model_executor/layers/fused_moe/flashinfer_trtllm_moe.py
+105
-1
@@ -3,7 +3,12 @@
3
4
import torch
5
6
-from vllm.model_executor.layers.fused_moe.config import RoutingMethodType
6
+import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
+from vllm.model_executor.layers.fused_moe.config import (
8
+ FusedMoEConfig,
9
+ FusedMoEParallelConfig,
10
+ RoutingMethodType,
11
+)
12
from vllm.model_executor.layers.fused_moe.utils import moe_kernel_quantize_input
13
from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
14
calculate_tile_tokens_dim,
@@ -11,8 +16,107 @@ from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
16
from vllm.model_executor.layers.quantization.utils.fp8_utils import (
17
per_token_group_quant_fp8,
18
)
19
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
20
+ QuantKey,
21
+ kFp8Dynamic128Sym,
22
+ kFp8Static128BlockSym,
23
+ kFp8StaticTensorSym,
24
+)
25
+from vllm.platforms import current_platform
26
from vllm.utils.torch_utils import direct_register_custom_op
27
28
+#
29
+# Methods used by the oracle for kernel selection.
30
+#
31
+
32
+
33
+def _supports_current_device() -> bool:
34
+ """Supports only Blackwell-family GPUs."""
35
+ p = current_platform
36
+ # Add check flashinfer trtllm is available
37
+ return p.is_cuda() and p.is_device_capability_family(100)
38
+
39
+
40
+def _supports_no_act_and_mul() -> bool:
41
+ """Does not support non-gated MoE (i.e. Nanotron-Mini)."""
42
+ return False
43
+
44
+
45
+def _supports_quant_scheme(
46
+ weight_key: QuantKey | None,
47
+ activation_key: QuantKey | None,
48
+) -> bool:
49
+ """Supports Fp8 per-tensor and Fp8 block."""
50
+ SUPPORTED_W_A = [
51
+ (kFp8Static128BlockSym, kFp8Dynamic128Sym),
52
+ (kFp8StaticTensorSym, kFp8StaticTensorSym),
53
+ ]
54
+ return (weight_key, activation_key) in SUPPORTED_W_A
55
+
56
+
57
+def _supports_activation(activation: str) -> bool:
58
+ """Supports silu activation only."""
59
+ return activation in ["silu"]
60
+
61
+
62
+def _supports_routing_method(
63
+ weight_key: QuantKey | None,
64
+ activation_key: QuantKey | None,
65
+ routing_method: RoutingMethodType,
66
+) -> bool:
67
+ """Monolithic kernels need to express router support."""
68
+ if (weight_key, activation_key) == (kFp8Static128BlockSym, kFp8Dynamic128Sym):
69
+ # NOTE(rob): potentially allow others here. This is a conservative list.
70
+ return routing_method in [
71
+ RoutingMethodType.DeepSeekV3,
72
+ RoutingMethodType.Renormalize,
73
+ RoutingMethodType.RenormalizeNaive,
74
+ ]
75
+ elif (weight_key, activation_key) == (kFp8StaticTensorSym, kFp8StaticTensorSym):
76
+ # NOTE(rob): kernel requires Llama4.
77
+ return routing_method == RoutingMethodType.Llama4
78
+
79
+ else:
80
+ raise ValueError("Unsupported quantization scheme.")
81
+
82
+
83
+def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
84
+ """Supports TRTLLM Kernel does not support EPLB."""
85
+ return not moe_parallel_config.enable_eplb
86
+
87
+
88
+def is_supported_config_trtllm(
89
+ moe_config: FusedMoEConfig,
90
+ weight_key: QuantKey | None,
91
+ activation_key: QuantKey | None,
92
+ activation_format: mk.FusedMoEActivationFormat,
93
+) -> tuple[bool, str | None]:
94
+ """
95
+ This method mirrors mk.FusedMoEPermuteExpertsUnpermute.is_supported_config
96
+ """
97
+
98
+ def _make_reason(reason: str) -> str:
99
+ return f"kernel does not support {reason}"
100
+
101
+ if not _supports_current_device():
102
+ return False, _make_reason("current device")
103
+ elif not (moe_config.is_act_and_mul or _supports_no_act_and_mul()):
104
+ return False, _make_reason("no act_and_mul MLP layer")
105
+ elif not _supports_activation(moe_config.activation):
106
+ return False, _make_reason(f"{moe_config.activation} activation")
107
+ elif not _supports_quant_scheme(weight_key, activation_key):
108
+ return False, _make_reason("quantization scheme")
109
+ elif not _supports_parallel_config(moe_config.moe_parallel_config):
110
+ return False, _make_reason("parallel config")
111
+ elif not _supports_routing_method(
112
+ weight_key, activation_key, moe_config.routing_method
113
+ ):
114
+ return False, _make_reason("routing method")
115
+ elif activation_format != mk.FusedMoEActivationFormat.Standard:
116
+ return False, _make_reason("activation format")
117
+
118
+ return True, None
119
+
120
121
def flashinfer_fused_moe_blockscale_fp8(
122
routing_logits: torch.Tensor,
vllm/model_executor/layers/fused_moe/fused_batched_moe.py
+121
-26
@@ -5,7 +5,11 @@
5
import torch
6
7
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8
-from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
8
+from vllm.model_executor.layers.fused_moe.config import (
9
+ FusedMoEConfig,
10
+ FusedMoEParallelConfig,
11
+ FusedMoEQuantConfig,
12
+)
13
from vllm.model_executor.layers.fused_moe.fused_moe import try_get_optimal_moe_config
14
from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
15
TopKWeightAndReduceDelegate,
@@ -17,7 +21,17 @@ from vllm.model_executor.layers.fused_moe.utils import (
21
normalize_batched_scales_shape,
22
normalize_scales_shape,
23
)
20
-from vllm.model_executor.layers.quantization.utils.quant_utils import group_broadcast
24
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
25
+ QuantKey,
26
+ group_broadcast,
27
+ kFp8Dynamic128Sym,
28
+ kFp8DynamicTensorSym,
29
+ kFp8DynamicTokenSym,
30
+ kFp8Static128BlockSym,
31
+ kFp8StaticChannelSym,
32
+ kFp8StaticTensorSym,
33
+)
34
+from vllm.platforms import current_platform
35
from vllm.triton_utils import tl, triton
36
37
@@ -633,25 +647,62 @@ class NaiveBatchedExperts(mk.FusedMoEPermuteExpertsUnpermute):
647
648
def __init__(
649
self,
650
+ moe_config: FusedMoEConfig,
651
+ quant_config: FusedMoEQuantConfig,
652
max_num_tokens: int,
653
num_dispatchers: int,
638
- quant_config: FusedMoEQuantConfig,
654
):
640
- super().__init__(quant_config)
655
+ super().__init__(
656
+ moe_config=moe_config,
657
+ quant_config=quant_config,
658
+ max_num_tokens=max_num_tokens,
659
+ num_dispatchers=num_dispatchers,
660
+ )
661
assert not self.quant_config.use_int8_w8a8, "NYI"
662
assert not self.quant_config.use_int8_w8a16, "NYI"
663
assert not self.quant_config.use_int4_w4a16, "NYI"
664
assert self.quant_config.ocp_mx_scheme is None, "NYI"
645
- self.max_num_tokens = max_num_tokens
646
- self.num_dispatchers = num_dispatchers
665
648
- @property
649
- def activation_formats(
650
- self,
651
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
652
- return (
653
- mk.FusedMoEActivationFormat.BatchedExperts,
654
- mk.FusedMoEActivationFormat.BatchedExperts,
666
+ @staticmethod
667
+ def activation_format() -> mk.FusedMoEActivationFormat:
668
+ return mk.FusedMoEActivationFormat.BatchedExperts
669
+
670
+ @staticmethod
671
+ def _supports_current_device() -> bool:
672
+ raise NotImplementedError(
673
+ "NaiveBatchedExperts is not yet used by an Oracle. "
674
+ "This method should not be called."
675
+ )
676
+
677
+ @staticmethod
678
+ def _supports_no_act_and_mul() -> bool:
679
+ raise NotImplementedError(
680
+ "NaiveBatchedExperts is not yet used by an Oracle. "
681
+ "This method should not be called."
682
+ )
683
+
684
+ @staticmethod
685
+ def _supports_quant_scheme(
686
+ weight_key: QuantKey | None,
687
+ activation_key: QuantKey | None,
688
+ ) -> bool:
689
+ raise NotImplementedError(
690
+ "NaiveBatchedExperts is not yet used by an Oracle. "
691
+ "This method should not be called."
692
+ )
693
+
694
+ @staticmethod
695
+ def _supports_activation(activation: str) -> bool:
696
+ raise NotImplementedError(
697
+ "NaiveBatchedExperts is not yet used by an Oracle. "
698
+ "This method should not be called."
699
+ )
700
+
701
+ @staticmethod
702
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
703
+ raise NotImplementedError(
704
+ "NaiveBatchedExperts is not yet used by an Oracle. "
705
+ "This method should not be called."
706
)
707
708
def supports_chunking(self) -> bool:
@@ -675,6 +726,8 @@ class NaiveBatchedExperts(mk.FusedMoEPermuteExpertsUnpermute):
726
expert_tokens_meta: mk.ExpertTokensMetadata | None,
727
activation: str,
728
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
729
+ assert self.num_dispatchers is not None
730
+ assert self.max_num_tokens is not None
731
num_dp = self.num_dispatchers
732
num_experts = local_num_experts
733
workspace13 = (num_experts, self.max_num_tokens * num_dp, K)
@@ -826,29 +879,69 @@ class BatchedTritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
879
880
def __init__(
881
self,
882
+ moe_config: FusedMoEConfig,
883
+ quant_config: FusedMoEQuantConfig,
884
max_num_tokens: int,
885
num_dispatchers: int,
831
- quant_config: FusedMoEQuantConfig,
886
):
833
- super().__init__(quant_config)
887
+ super().__init__(
888
+ moe_config=moe_config,
889
+ quant_config=quant_config,
890
+ max_num_tokens=max_num_tokens,
891
+ num_dispatchers=num_dispatchers,
892
+ )
893
assert not self.quant_config.use_int8_w8a8, "NYI"
894
assert not self.quant_config.use_int8_w8a16, "NYI"
895
assert not self.quant_config.use_int4_w4a16, "NYI"
896
assert self.quant_config.ocp_mx_scheme is None, "NYI"
838
- assert max_num_tokens > 0
839
- assert num_dispatchers > 0
840
- self.max_num_tokens = max_num_tokens
841
- self.num_dispatchers = num_dispatchers
897
843
- @property
844
- def activation_formats(
845
- self,
846
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
847
- return (
848
- mk.FusedMoEActivationFormat.BatchedExperts,
849
- mk.FusedMoEActivationFormat.BatchedExperts,
898
+ @staticmethod
899
+ def activation_format() -> mk.FusedMoEActivationFormat:
900
+ return mk.FusedMoEActivationFormat.BatchedExperts
901
+
902
+ @staticmethod
903
+ def _supports_current_device() -> bool:
904
+ return current_platform.is_cuda_alike()
905
+
906
+ @staticmethod
907
+ def _supports_no_act_and_mul() -> bool:
908
+ return False
909
+
910
+ @staticmethod
911
+ def _supports_quant_scheme(
912
+ weight_key: QuantKey | None,
913
+ activation_key: QuantKey | None,
914
+ ) -> bool:
915
+ p = current_platform
916
+ device_supports_fp8 = (p.is_rocm() and p.rocm.on_gfx9()) or (
917
+ p.is_cuda() and p.has_device_capability((8, 9))
918
+ )
919
+
920
+ SUPPORTED_W_A_FP8 = [
921
+ (kFp8Static128BlockSym, kFp8Dynamic128Sym),
922
+ (kFp8StaticChannelSym, kFp8DynamicTokenSym),
923
+ (kFp8StaticTensorSym, kFp8StaticTensorSym),
924
+ (kFp8StaticTensorSym, kFp8DynamicTensorSym),
925
+ ]
926
+ return (weight_key, activation_key) == (None, None) or (
927
+ device_supports_fp8 and (weight_key, activation_key) in SUPPORTED_W_A_FP8
928
)
929
930
+ @staticmethod
931
+ def _supports_activation(activation: str) -> bool:
932
+ return activation in [
933
+ "silu",
934
+ "gelu",
935
+ "swigluoai",
936
+ "silu_no_mul",
937
+ "gelu_no_mul",
938
+ "relu2_no_mul",
939
+ ]
940
+
941
+ @staticmethod
942
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
943
+ return True
944
+
945
def supports_chunking(self) -> bool:
946
return False
947
@@ -870,6 +963,8 @@ class BatchedTritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
963
expert_tokens_meta: mk.ExpertTokensMetadata | None,
964
activation: str,
965
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
966
+ assert self.num_dispatchers is not None
967
+ assert self.max_num_tokens is not None
968
num_dp = self.num_dispatchers
969
num_experts = local_num_experts
970
max_num_tokens = self.max_num_tokens
vllm/model_executor/layers/fused_moe/fused_marlin_moe.py
+80
-46
@@ -8,7 +8,11 @@ import torch
8
9
import vllm._custom_ops as ops
10
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
11
-from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
11
+from vllm.model_executor.layers.fused_moe.config import (
12
+ FusedMoEConfig,
13
+ FusedMoEParallelConfig,
14
+ FusedMoEQuantConfig,
15
+)
16
from vllm.model_executor.layers.fused_moe.moe_align_block_size import (
17
batched_moe_align_block_size,
18
moe_align_block_size,
@@ -27,6 +31,13 @@ from vllm.model_executor.layers.quantization.utils.marlin_utils import (
31
marlin_moe_intermediate_size,
32
marlin_quant_input,
33
)
34
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
35
+ QuantKey,
36
+ kFp8Static128BlockSym,
37
+ kFp8StaticChannelSym,
38
+ kFp8StaticTensorSym,
39
+ kNvfp4Static,
40
+)
41
from vllm.platforms import current_platform
42
from vllm.scalar_type import ScalarType, scalar_types
43
@@ -522,7 +533,10 @@ def batched_fused_marlin_moe(
533
class MarlinExpertsBase(mk.FusedMoEPermuteExpertsUnpermute):
534
def __init__(
535
self,
536
+ moe_config: FusedMoEConfig,
537
quant_config: FusedMoEQuantConfig,
538
+ max_num_tokens: int | None = None,
539
+ num_dispatchers: int | None = None,
540
w13_g_idx: torch.Tensor | None = None,
541
w2_g_idx: torch.Tensor | None = None,
542
w13_g_idx_sort_indices: torch.Tensor | None = None,
@@ -541,7 +555,51 @@ class MarlinExpertsBase(mk.FusedMoEPermuteExpertsUnpermute):
555
self.w13_g_idx_sort_indices = w13_g_idx_sort_indices
556
self.w2_g_idx_sort_indices = w2_g_idx_sort_indices
557
self.is_k_full = is_k_full
544
- super().__init__(quant_config)
558
+ super().__init__(
559
+ moe_config=moe_config,
560
+ quant_config=quant_config,
561
+ max_num_tokens=max_num_tokens,
562
+ num_dispatchers=num_dispatchers,
563
+ )
564
+
565
+ @staticmethod
566
+ def _supports_current_device() -> bool:
567
+ p = current_platform
568
+ return p.is_cuda() and p.has_device_capability((7, 5))
569
+
570
+ @staticmethod
571
+ def _supports_no_act_and_mul() -> bool:
572
+ return False
573
+
574
+ @staticmethod
575
+ def _supports_quant_scheme(
576
+ weight_key: QuantKey | None,
577
+ activation_key: QuantKey | None,
578
+ ) -> bool:
579
+ # TODO(rob): add int4, mxfp4, int8 as integrations
580
+ # are migrated to use the oracle one-by-one.
581
+ SUPPORTED_W = [
582
+ kFp8Static128BlockSym,
583
+ kFp8StaticChannelSym,
584
+ kFp8StaticTensorSym,
585
+ kNvfp4Static,
586
+ ]
587
+ return weight_key in SUPPORTED_W
588
+
589
+ @staticmethod
590
+ def _supports_activation(activation: str) -> bool:
591
+ return activation in [
592
+ "silu",
593
+ "gelu",
594
+ "swigluoai",
595
+ "silu_no_mul",
596
+ "gelu_no_mul",
597
+ "relu2_no_mul",
598
+ ]
599
+
600
+ @staticmethod
601
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
602
+ return True
603
604
@property
605
def quant_type_id(self) -> int:
@@ -587,38 +645,15 @@ class MarlinExpertsBase(mk.FusedMoEPermuteExpertsUnpermute):
645
646
647
class MarlinExperts(MarlinExpertsBase):
590
- def __init__(
591
- self,
592
- quant_config: FusedMoEQuantConfig,
593
- w13_g_idx: torch.Tensor | None = None,
594
- w2_g_idx: torch.Tensor | None = None,
595
- w13_g_idx_sort_indices: torch.Tensor | None = None,
596
- w2_g_idx_sort_indices: torch.Tensor | None = None,
597
- is_k_full: bool = True,
598
- ):
599
- super().__init__(
600
- quant_config,
601
- w13_g_idx,
602
- w2_g_idx,
603
- w13_g_idx_sort_indices,
604
- w2_g_idx_sort_indices,
605
- is_k_full,
606
- )
607
-
648
def supports_expert_map(self) -> bool:
649
return True
650
651
def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
652
return TopKWeightAndReduceNoOP()
653
614
- @property
615
- def activation_formats(
616
- self,
617
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
618
- return (
619
- mk.FusedMoEActivationFormat.Standard,
620
- mk.FusedMoEActivationFormat.Standard,
621
- )
654
+ @staticmethod
655
+ def activation_format() -> mk.FusedMoEActivationFormat:
656
+ return mk.FusedMoEActivationFormat.Standard
657
658
def supports_chunking(self) -> bool:
659
return True
@@ -714,9 +749,10 @@ class MarlinExperts(MarlinExpertsBase):
749
class BatchedMarlinExperts(MarlinExpertsBase):
750
def __init__(
751
self,
752
+ moe_config: FusedMoEConfig,
753
+ quant_config: FusedMoEQuantConfig,
754
max_num_tokens: int,
755
num_dispatchers: int,
719
- quant_config: FusedMoEQuantConfig,
756
w13_g_idx: torch.Tensor | None = None,
757
w2_g_idx: torch.Tensor | None = None,
758
w13_g_idx_sort_indices: torch.Tensor | None = None,
@@ -724,15 +760,16 @@ class BatchedMarlinExperts(MarlinExpertsBase):
760
is_k_full: bool = True,
761
):
762
super().__init__(
727
- quant_config,
728
- w13_g_idx,
729
- w2_g_idx,
730
- w13_g_idx_sort_indices,
731
- w2_g_idx_sort_indices,
732
- is_k_full,
763
+ moe_config=moe_config,
764
+ quant_config=quant_config,
765
+ max_num_tokens=max_num_tokens,
766
+ num_dispatchers=num_dispatchers,
767
+ w13_g_idx=w13_g_idx,
768
+ w2_g_idx=w2_g_idx,
769
+ w13_g_idx_sort_indices=w13_g_idx_sort_indices,
770
+ w2_g_idx_sort_indices=w2_g_idx_sort_indices,
771
+ is_k_full=is_k_full,
772
)
734
- self.max_num_tokens = max_num_tokens
735
- self.num_dispatchers = num_dispatchers
773
774
def supports_expert_map(self) -> bool:
775
return True
@@ -740,14 +777,9 @@ class BatchedMarlinExperts(MarlinExpertsBase):
777
def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
778
return TopKWeightAndReduceDelegate()
779
743
- @property
744
- def activation_formats(
745
- self,
746
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
747
- return (
748
- mk.FusedMoEActivationFormat.BatchedExperts,
749
- mk.FusedMoEActivationFormat.BatchedExperts,
750
- )
780
+ @staticmethod
781
+ def activation_format() -> mk.FusedMoEActivationFormat:
782
+ return mk.FusedMoEActivationFormat.BatchedExperts
783
784
def supports_chunking(self) -> bool:
785
return False
@@ -763,9 +795,11 @@ class BatchedMarlinExperts(MarlinExpertsBase):
795
expert_tokens_meta: mk.ExpertTokensMetadata | None,
796
activation: str,
797
) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
798
+ assert self.num_dispatchers is not None
799
+ assert self.max_num_tokens is not None
800
num_dispatchers = self.num_dispatchers
801
num_experts = local_num_experts
768
- max_num_tokens = M if self.max_num_tokens is None else self.max_num_tokens
802
+ max_num_tokens = self.max_num_tokens
803
workspace13 = (num_experts * max_num_tokens * num_dispatchers, max(K, N * 2))
804
workspace2 = (num_experts * max_num_tokens * num_dispatchers, N)
805
output = (num_experts, max_num_tokens * num_dispatchers, K)
vllm/model_executor/layers/fused_moe/fused_moe.py
+120
-77
@@ -19,13 +19,11 @@ from vllm.model_executor.layers.batch_invariant import (
19
)
20
from vllm.model_executor.layers.fused_moe.config import (
21
FUSED_MOE_UNQUANTIZED_CONFIG,
22
+ FusedMoEConfig,
23
+ FusedMoEParallelConfig,
24
FusedMoEQuantConfig,
25
_get_config_dtype_str,
26
)
25
-from vllm.model_executor.layers.fused_moe.deep_gemm_moe import (
26
- _valid_deep_gemm,
27
- deep_gemm_moe_fp8,
28
-)
27
from vllm.model_executor.layers.fused_moe.moe_align_block_size import (
28
moe_align_block_size,
29
)
@@ -44,9 +42,16 @@ from vllm.model_executor.layers.fused_moe.utils import (
42
from vllm.model_executor.layers.quantization.utils.mxfp4_utils import dequant_mxfp4
43
from vllm.model_executor.layers.quantization.utils.mxfp6_utils import dequant_mxfp6
44
from vllm.model_executor.layers.quantization.utils.ocp_mx_utils import OCP_MX_Scheme
45
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
46
+ QuantKey,
47
+ kFp8Dynamic128Sym,
48
+ kFp8DynamicTokenSym,
49
+ kFp8Static128BlockSym,
50
+ kFp8StaticChannelSym,
51
+ kFp8StaticTensorSym,
52
+)
53
from vllm.platforms import current_platform
54
from vllm.triton_utils import tl, triton
49
-from vllm.utils.deep_gemm import is_deep_gemm_e8m0_used
55
from vllm.utils.torch_utils import direct_register_custom_op, is_torch_equal_or_newer
56
57
logger = init_logger(__name__)
@@ -1534,66 +1539,36 @@ def fused_experts(
1539
global_num_experts: int = -1,
1540
expert_map: torch.Tensor | None = None,
1541
quant_config: FusedMoEQuantConfig | None = None,
1537
- allow_deep_gemm: bool = False,
1542
) -> torch.Tensor:
1543
if quant_config is None:
1544
quant_config = FUSED_MOE_UNQUANTIZED_CONFIG
1545
1542
- # For now, disable DeepGemm for small N (<= 512) until better
1543
- # permute/unpermute ops are available.
1544
- # However, on B200, we use DeepGemm for all cases because they only support
1545
- # E8M0 scale, which means we requantize the weight and input to the specific
1546
- # scale. Fallen back to cutlass or triton for some cases would cause
1547
- # accuracy issue.
1548
- if (
1549
- allow_deep_gemm
1550
- and quant_config.use_fp8_w8a8
1551
- and (is_deep_gemm_e8m0_used() or _valid_deep_gemm(hidden_states, w1, w2))
1552
- ):
1553
- assert quant_config is not None
1554
- return deep_gemm_moe_fp8(
1555
- hidden_states=hidden_states,
1556
- w1=w1,
1557
- w2=w2,
1558
- topk_weights=topk_weights,
1559
- topk_ids=topk_ids,
1560
- inplace=inplace,
1561
- activation=activation,
1562
- global_num_experts=global_num_experts,
1563
- expert_map=expert_map,
1564
- w1_scale=quant_config.w1_scale,
1565
- w2_scale=quant_config.w2_scale,
1566
- a1_scale=quant_config.a1_scale,
1567
- a2_scale=quant_config.a2_scale,
1568
- apply_router_weight_on_input=apply_router_weight_on_input,
1569
- )
1570
- else:
1571
- return dispatch_fused_experts_func(inplace)(
1572
- hidden_states=hidden_states,
1573
- w1=w1,
1574
- w2=w2,
1575
- topk_weights=topk_weights,
1576
- topk_ids=topk_ids,
1577
- activation=activation,
1578
- apply_router_weight_on_input=apply_router_weight_on_input,
1579
- use_fp8_w8a8=quant_config.use_fp8_w8a8,
1580
- use_int8_w8a8=quant_config.use_int8_w8a8,
1581
- use_int8_w8a16=quant_config.use_int8_w8a16,
1582
- use_int4_w4a16=quant_config.use_int4_w4a16,
1583
- ocp_mx_scheme=quant_config.ocp_mx_scheme,
1584
- per_channel_quant=quant_config.per_act_token_quant,
1585
- global_num_experts=global_num_experts,
1586
- expert_map=expert_map,
1587
- w1_scale=quant_config.w1_scale,
1588
- w2_scale=quant_config.w2_scale,
1589
- w1_zp=quant_config.w1_zp,
1590
- w2_zp=quant_config.w2_zp,
1591
- a1_scale=quant_config.a1_scale,
1592
- a2_scale=quant_config.a2_scale,
1593
- block_shape=quant_config.block_shape,
1594
- w1_bias=quant_config.w1_bias,
1595
- w2_bias=quant_config.w2_bias,
1596
- )
1546
+ return dispatch_fused_experts_func(inplace)(
1547
+ hidden_states=hidden_states,
1548
+ w1=w1,
1549
+ w2=w2,
1550
+ topk_weights=topk_weights,
1551
+ topk_ids=topk_ids,
1552
+ activation=activation,
1553
+ apply_router_weight_on_input=apply_router_weight_on_input,
1554
+ use_fp8_w8a8=quant_config.use_fp8_w8a8,
1555
+ use_int8_w8a8=quant_config.use_int8_w8a8,
1556
+ use_int8_w8a16=quant_config.use_int8_w8a16,
1557
+ use_int4_w4a16=quant_config.use_int4_w4a16,
1558
+ ocp_mx_scheme=quant_config.ocp_mx_scheme,
1559
+ per_channel_quant=quant_config.per_act_token_quant,
1560
+ global_num_experts=global_num_experts,
1561
+ expert_map=expert_map,
1562
+ w1_scale=quant_config.w1_scale,
1563
+ w2_scale=quant_config.w2_scale,
1564
+ w1_zp=quant_config.w1_zp,
1565
+ w2_zp=quant_config.w2_zp,
1566
+ a1_scale=quant_config.a1_scale,
1567
+ a2_scale=quant_config.a2_scale,
1568
+ block_shape=quant_config.block_shape,
1569
+ w1_bias=quant_config.w1_bias,
1570
+ w2_bias=quant_config.w2_bias,
1571
+ )
1572
1573
1574
def _get_config_quant_dtype(
@@ -1924,19 +1899,53 @@ def fused_experts_impl(
1899
class TritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
1900
def __init__(
1901
self,
1902
+ moe_config: FusedMoEConfig,
1903
quant_config: FusedMoEQuantConfig,
1904
):
1929
- super().__init__(quant_config)
1930
-
1931
- @property
1932
- def activation_formats(
1933
- self,
1934
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
1935
- return (
1936
- mk.FusedMoEActivationFormat.Standard,
1937
- mk.FusedMoEActivationFormat.Standard,
1905
+ super().__init__(moe_config, quant_config)
1906
+
1907
+ @staticmethod
1908
+ def activation_format() -> mk.FusedMoEActivationFormat:
1909
+ return mk.FusedMoEActivationFormat.Standard
1910
+
1911
+ @staticmethod
1912
+ def _supports_current_device() -> bool:
1913
+ return current_platform.is_cuda_alike()
1914
+
1915
+ @staticmethod
1916
+ def _supports_no_act_and_mul() -> bool:
1917
+ return False
1918
+
1919
+ @staticmethod
1920
+ def _supports_quant_scheme(
1921
+ weight_key: QuantKey | None,
1922
+ activation_key: QuantKey | None,
1923
+ ) -> bool:
1924
+ p = current_platform
1925
+ device_supports_fp8 = (p.is_rocm() and p.rocm.on_gfx9()) or (
1926
+ p.is_cuda() and p.has_device_capability((8, 9))
1927
)
1928
1929
+ if not device_supports_fp8:
1930
+ return (weight_key, activation_key) == (None, None)
1931
+
1932
+ SUPPORTED_W_A = [
1933
+ (None, None),
1934
+ (kFp8Static128BlockSym, kFp8Dynamic128Sym),
1935
+ (kFp8StaticChannelSym, kFp8DynamicTokenSym),
1936
+ (kFp8StaticTensorSym, kFp8DynamicTokenSym),
1937
+ (kFp8StaticTensorSym, kFp8StaticTensorSym),
1938
+ ]
1939
+ return (weight_key, activation_key) in SUPPORTED_W_A
1940
+
1941
+ @staticmethod
1942
+ def _supports_activation(activation: str) -> bool:
1943
+ return activation in ["silu", "gelu", "swigluoai"]
1944
+
1945
+ @staticmethod
1946
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
1947
+ return True
1948
+
1949
def supports_chunking(self) -> bool:
1950
return True
1951
@@ -2111,11 +2120,43 @@ class TritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
2120
2121
2122
class TritonWNA16Experts(TritonExperts):
2114
- def __init__(
2115
- self,
2116
- quant_config: FusedMoEQuantConfig,
2117
- ):
2118
- super().__init__(quant_config)
2123
+ @staticmethod
2124
+ def _supports_current_device() -> bool:
2125
+ raise NotImplementedError(
2126
+ "TritonWNA16Experts is not yet used by an Oracle. "
2127
+ "This method should not be called."
2128
+ )
2129
+
2130
+ @staticmethod
2131
+ def _supports_no_act_and_mul() -> bool:
2132
+ raise NotImplementedError(
2133
+ "TritonWNA16Experts is not yet used by an Oracle. "
2134
+ "This method should not be called."
2135
+ )
2136
+
2137
+ @staticmethod
2138
+ def _supports_quant_scheme(
2139
+ weight_key: QuantKey | None,
2140
+ activation_key: QuantKey | None,
2141
+ ) -> bool:
2142
+ raise NotImplementedError(
2143
+ "TritonWNA16Experts is not yet used by an Oracle. "
2144
+ "This method should not be called."
2145
+ )
2146
+
2147
+ @staticmethod
2148
+ def _supports_activation(activation: str) -> bool:
2149
+ raise NotImplementedError(
2150
+ "TritonWNA16Experts is not yet used by an Oracle. "
2151
+ "This method should not be called."
2152
+ )
2153
+
2154
+ @staticmethod
2155
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
2156
+ raise NotImplementedError(
2157
+ "TritonWNA16Experts is not yet used by an Oracle. "
2158
+ "This method should not be called."
2159
+ )
2160
2161
def apply(
2162
self,
@@ -2254,10 +2295,12 @@ class TritonWNA16Experts(TritonExperts):
2295
2296
2297
def modular_triton_fused_moe(
2257
- quant_config: FusedMoEQuantConfig, shared_experts: torch.nn.Module | None = None
2298
+ moe_config: FusedMoEConfig,
2299
+ quant_config: FusedMoEQuantConfig,
2300
+ shared_experts: torch.nn.Module | None = None,
2301
) -> mk.FusedMoEModularKernel:
2302
return mk.FusedMoEModularKernel(
2303
MoEPrepareAndFinalizeNoEP(),
2261
- TritonExperts(quant_config),
2304
+ TritonExperts(moe_config, quant_config),
2305
shared_experts,
2306
)
vllm/model_executor/layers/fused_moe/gpt_oss_triton_kernels_moe.py
+47
-28
@@ -9,12 +9,16 @@ from vllm import _custom_ops as ops
9
from vllm.logger import init_logger
10
from vllm.model_executor.layers.fused_moe.config import (
11
FUSED_MOE_UNQUANTIZED_CONFIG,
12
+ FusedMoEParallelConfig,
13
FusedMoEQuantConfig,
14
)
15
from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
16
TopKWeightAndReduceNoOP,
17
)
18
from vllm.model_executor.layers.fused_moe.utils import _resize_cache
19
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
20
+ QuantKey,
21
+)
22
from vllm.triton_utils import tl, triton
23
from vllm.utils.import_utils import has_triton_kernels
24
@@ -241,8 +245,43 @@ def make_routing_data(
245
246
247
class BaseOAITritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
244
- def __init__(self, quant_config: FusedMoEQuantConfig):
245
- super().__init__(quant_config)
248
+ @staticmethod
249
+ def _supports_current_device() -> bool:
250
+ raise NotImplementedError(
251
+ "OAITritonExperts is not yet used by an Oracle. "
252
+ "This method should not be called."
253
+ )
254
+
255
+ @staticmethod
256
+ def _supports_no_act_and_mul() -> bool:
257
+ raise NotImplementedError(
258
+ "OAITritonExperts is not yet used by an Oracle. "
259
+ "This method should not be called."
260
+ )
261
+
262
+ @staticmethod
263
+ def _supports_quant_scheme(
264
+ weight_key: QuantKey | None,
265
+ activation_key: QuantKey | None,
266
+ ) -> bool:
267
+ raise NotImplementedError(
268
+ "OAITritonExperts is not yet used by an Oracle. "
269
+ "This method should not be called."
270
+ )
271
+
272
+ @staticmethod
273
+ def _supports_activation(activation: str) -> bool:
274
+ raise NotImplementedError(
275
+ "OAITritonExperts is not yet used by an Oracle. "
276
+ "This method should not be called."
277
+ )
278
+
279
+ @staticmethod
280
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
281
+ raise NotImplementedError(
282
+ "OAITritonExperts is not yet used by an Oracle. "
283
+ "This method should not be called."
284
+ )
285
286
def supports_expert_map(self) -> bool:
287
return True
@@ -297,19 +336,9 @@ class BaseOAITritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
336
337
338
class OAITritonExperts(BaseOAITritonExperts):
300
- def __init__(self, quant_config: FusedMoEQuantConfig):
301
- # TODO (varun) : Enable activation quantization
302
- assert quant_config.use_mxfp4_w4a16, "Supports only mxfp4_w4a16"
303
- super().__init__(quant_config)
304
-
305
- @property
306
- def activation_formats(
307
- self,
308
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
309
- return (
310
- mk.FusedMoEActivationFormat.Standard,
311
- mk.FusedMoEActivationFormat.Standard,
312
- )
339
+ @staticmethod
340
+ def activation_format() -> mk.FusedMoEActivationFormat:
341
+ return mk.FusedMoEActivationFormat.Standard
342
343
def supports_chunking(self) -> bool:
344
return True
@@ -391,19 +420,9 @@ class UnfusedOAITritonExperts(BaseOAITritonExperts):
420
One use case for it is to inject LoRA modules on the activation and moe_sum.
421
"""
422
394
- def __init__(self, quant_config: FusedMoEQuantConfig):
395
- # TODO (varun) : Enable activation quantization
396
- assert quant_config.use_mxfp4_w4a16, "Supports only mxfp4_w4a16"
397
- super().__init__(quant_config)
398
-
399
- @property
400
- def activation_formats(
401
- self,
402
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
403
- return (
404
- mk.FusedMoEActivationFormat.Standard,
405
- mk.FusedMoEActivationFormat.Standard,
406
- )
423
+ @staticmethod
424
+ def activation_format() -> mk.FusedMoEActivationFormat:
425
+ return mk.FusedMoEActivationFormat.Standard
426
427
def supports_chunking(self) -> bool:
428
return True
vllm/model_executor/layers/fused_moe/layer.py
+36
-34
@@ -330,7 +330,6 @@ class FusedMoE(CustomOp):
330
is_sequence_parallel=False,
331
expert_mapping: list[tuple[str, str, int, str]] | None = None,
332
n_shared_experts: int | None = None,
333
- routing_method_type: RoutingMethodType | None = None,
333
router_logits_dtype: torch.dtype | None = None,
334
):
335
super().__init__()
@@ -519,10 +518,43 @@ class FusedMoE(CustomOp):
518
self.apply_router_weight_on_input = apply_router_weight_on_input
519
self.activation = activation
520
521
+ # TODO(bnell): in next PR move capture back to layer
522
+ capture: Callable[[torch.Tensor], None] | None = None
523
+ if (
524
+ self.vllm_config.model_config is not None
525
+ and self.vllm_config.model_config.enable_return_routed_experts
526
+ ):
527
+ # In dummy runs, the capturer is not initialized.
528
+ capturer = RoutedExpertsCapturer.get_instance()
529
+ if capturer is not None:
530
+ capture = lambda topk_ids: capturer.capture(self.layer_id, topk_ids)
531
+
532
+ self.router = create_fused_moe_router(
533
+ top_k=top_k,
534
+ global_num_experts=self.global_num_experts,
535
+ eplb_state=self.eplb_state,
536
+ renormalize=renormalize,
537
+ use_grouped_topk=use_grouped_topk,
538
+ num_expert_group=num_expert_group,
539
+ topk_group=topk_group,
540
+ custom_routing_function=custom_routing_function,
541
+ scoring_func=scoring_func,
542
+ routed_scaling_factor=routed_scaling_factor,
543
+ e_score_correction_bias=e_score_correction_bias,
544
+ num_fused_shared_experts=self.num_fused_shared_experts,
545
+ enable_eplb=enable_eplb,
546
+ # TODO(bnell): once we can construct the MK at init time, we
547
+ # can make this a value.
548
+ indices_type_getter=lambda: self.quant_method.topk_indices_dtype,
549
+ capture=capture,
550
+ )
551
+ self.routing_method_type: RoutingMethodType = self.router.routing_method_type
552
+
553
self.moe_config: FusedMoEConfig = FusedMoEConfig(
554
num_experts=self.global_num_experts,
555
experts_per_token=top_k,
556
hidden_dim=hidden_size,
557
+ intermediate_size_per_partition=self.intermediate_size_per_partition,
558
num_local_experts=self.local_num_experts,
559
moe_parallel_config=self.moe_parallel_config,
560
in_dtype=moe_in_dtype,
@@ -531,6 +563,9 @@ class FusedMoE(CustomOp):
563
has_bias=has_bias,
564
is_act_and_mul=is_act_and_mul,
565
is_lora_enabled=vllm_config.lora_config is not None,
566
+ activation=activation,
567
+ device=vllm_config.device_config.device,
568
+ routing_method=self.routing_method_type,
569
)
570
self.moe_config_use_flashinfer_cutlass_kernels = (
571
self.moe_config.use_flashinfer_cutlass_kernels
@@ -594,39 +629,6 @@ class FusedMoE(CustomOp):
629
self.batched_hidden_states: torch.Tensor | None = None
630
self.batched_router_logits: torch.Tensor | None = None
631
597
- # TODO(bnell): in next PR move capture back to layer
598
- capture: Callable[[torch.Tensor], None] | None = None
599
- if (
600
- self.vllm_config.model_config is not None
601
- and self.vllm_config.model_config.enable_return_routed_experts
602
- ):
603
- # In dummy runs, the capturer is not initialized.
604
- capturer = RoutedExpertsCapturer.get_instance()
605
- if capturer is not None:
606
- capture = lambda topk_ids: capturer.capture(self.layer_id, topk_ids)
607
-
608
- self.router = create_fused_moe_router(
609
- top_k=top_k,
610
- global_num_experts=self.global_num_experts,
611
- eplb_state=self.eplb_state,
612
- renormalize=renormalize,
613
- use_grouped_topk=use_grouped_topk,
614
- num_expert_group=num_expert_group,
615
- topk_group=topk_group,
616
- custom_routing_function=custom_routing_function,
617
- scoring_func=scoring_func,
618
- routed_scaling_factor=routed_scaling_factor,
619
- e_score_correction_bias=e_score_correction_bias,
620
- num_fused_shared_experts=self.num_fused_shared_experts,
621
- enable_eplb=enable_eplb,
622
- # TODO(bnell): once we can construct the MK at init time, we
623
- # can make this a value.
624
- indices_type_getter=lambda: self.quant_method.topk_indices_dtype,
625
- routing_method_type=routing_method_type,
626
- capture=capture,
627
- )
628
- self.routing_method_type: RoutingMethodType = self.router.routing_method_type
629
-
632
# Note: maybe_init_modular_kernel should only be called by
633
# prepare_communication_buffer_for_model.
634
# This is called after all weight loading and post-processing, so it
vllm/model_executor/layers/fused_moe/modular_kernel.py
+115
-6
@@ -13,6 +13,7 @@ import vllm.envs as envs
13
from vllm.forward_context import get_forward_context, is_forward_context_available
14
from vllm.logger import init_logger
15
from vllm.model_executor.layers.fused_moe.config import (
16
+ FusedMoEConfig,
17
FusedMoEParallelConfig,
18
FusedMoEQuantConfig,
19
)
@@ -22,6 +23,9 @@ from vllm.model_executor.layers.fused_moe.utils import (
23
count_expert_num_tokens,
24
disable_inplace,
25
)
26
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
27
+ QuantKey,
28
+)
29
from vllm.utils.math_utils import cdiv
30
from vllm.v1.worker.ubatching import (
31
dbo_enabled,
@@ -374,18 +378,51 @@ class FusedMoEPermuteExpertsUnpermute(ABC):
378
379
def __init__(
380
self,
381
+ moe_config: FusedMoEConfig,
382
quant_config: FusedMoEQuantConfig,
383
+ max_num_tokens: int | None = None,
384
+ num_dispatchers: int | None = None,
385
):
386
"""
387
+ moe_config: MoE layer configuration.
388
quant_config: Quantization parameters for this experts instance.
389
"""
390
+ if self.activation_format() == FusedMoEActivationFormat.Standard and (
391
+ max_num_tokens is not None or num_dispatchers is not None
392
+ ):
393
+ raise ValueError(
394
+ "max_num_tokens and num_dispatchers should only be set for "
395
+ "BatchedExperts activation format."
396
+ )
397
+ elif self.activation_format() == FusedMoEActivationFormat.BatchedExperts and (
398
+ max_num_tokens is None or num_dispatchers is None
399
+ ):
400
+ raise ValueError(
401
+ "max_num_tokens and num_dispatchers must be set for "
402
+ "BatchedExperts activation format."
403
+ )
404
+
405
+ self.moe_config = moe_config
406
self.quant_config = quant_config
407
+ self.max_num_tokens = max_num_tokens
408
+ self.num_dispatchers = num_dispatchers
409
384
- @property
410
+ @staticmethod
411
+ def expects_unquantized_inputs(
412
+ moe_config: FusedMoEConfig, quant_config: FusedMoEQuantConfig
413
+ ) -> bool:
414
+ """
415
+ Whether or not the PrepareFinalize should defer input quantization
416
+ in the prepare step. If True, then the Experts kernel will
417
+ execute the input quantization itself.
418
+
419
+ Sample subclasses that override are AITER and FlashInfer CUTLASS.
420
+ """
421
+ return False
422
+
423
+ @staticmethod
424
@abstractmethod
386
- def activation_formats(
387
- self,
388
- ) -> tuple[FusedMoEActivationFormat, FusedMoEActivationFormat]:
425
+ def activation_format() -> FusedMoEActivationFormat:
426
"""
427
A property which is a tuple of the input and output activation formats
428
for the 'apply' method.
@@ -435,6 +472,78 @@ class FusedMoEPermuteExpertsUnpermute(ABC):
472
473
return E, M, N, K, topk
474
475
+ #
476
+ # Various helpers for registering support for various features.
477
+ # Used by the oracle to select a particular kernel for a deployment.
478
+ #
479
+
480
+ @staticmethod
481
+ def is_supported_config(
482
+ cls: type["FusedMoEPermuteExpertsUnpermute"],
483
+ moe_config: FusedMoEConfig,
484
+ weight_key: QuantKey | None,
485
+ activation_key: QuantKey | None,
486
+ activation_format: FusedMoEActivationFormat,
487
+ ) -> tuple[bool, str | None]:
488
+ def _make_reason(reason: str) -> str:
489
+ return f"kernel does not support {reason}"
490
+
491
+ if not cls._supports_current_device():
492
+ return False, _make_reason("current device")
493
+ elif not (moe_config.is_act_and_mul or cls._supports_no_act_and_mul()):
494
+ return False, _make_reason("no act_and_mul MLP layer")
495
+ elif not cls._supports_activation(moe_config.activation):
496
+ return False, _make_reason(f"{moe_config.activation} activation")
497
+ elif not cls._supports_quant_scheme(weight_key, activation_key):
498
+ return False, _make_reason("quantization scheme")
499
+ elif not cls._supports_parallel_config(moe_config.moe_parallel_config):
500
+ return False, _make_reason("parallel config")
501
+ elif activation_format != cls.activation_format():
502
+ return False, _make_reason(f"{activation_format.value} activation format")
503
+ return True, None
504
+
505
+ @staticmethod
506
+ @abstractmethod
507
+ def _supports_current_device() -> bool:
508
+ """
509
+ Whether the kernel supports the current device type
510
+ (compute cability and current platform).
511
+ """
512
+ raise NotImplementedError
513
+
514
+ @staticmethod
515
+ @abstractmethod
516
+ def _supports_no_act_and_mul() -> bool:
517
+ """
518
+ Whether the kernel supports act_and_mul=False, i.e.
519
+ non-gated MoE models like Nemotron-Nano.
520
+ """
521
+ raise NotImplementedError
522
+
523
+ @staticmethod
524
+ @abstractmethod
525
+ def _supports_quant_scheme(
526
+ weight_key: QuantKey | None,
527
+ activation_key: QuantKey | None,
528
+ ) -> bool:
529
+ raise NotImplementedError
530
+
531
+ @staticmethod
532
+ @abstractmethod
533
+ def _supports_activation(activation: str) -> bool:
534
+ """
535
+ Whether the kernel supports a particular act function.
536
+ """
537
+ raise NotImplementedError
538
+
539
+ @staticmethod
540
+ @abstractmethod
541
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
542
+ """
543
+ Whether the kernel supports deployment in expert parallel.
544
+ """
545
+ raise NotImplementedError
546
+
547
#
548
# Various helpers for accessing quantization parameters from the
549
# quant_config.
@@ -715,12 +824,12 @@ class FusedMoEModularKernel(torch.nn.Module):
824
825
self._post_init_setup()
826
assert (
718
- prepare_finalize.activation_format == fused_experts.activation_formats[0]
827
+ prepare_finalize.activation_format == fused_experts.activation_format()
828
), (
829
f"{prepare_finalize.__class__.__name__}."
830
f"{prepare_finalize.activation_format} == "
831
f"{fused_experts.__class__.__name__}."
723
- f"{fused_experts.activation_formats[0]}"
832
+ f"{fused_experts.activation_format()}"
833
)
834
835
def _post_init_setup(self):
vllm/model_executor/layers/fused_moe/oracle/fp8.py
+350
-186
@@ -14,6 +14,12 @@ from vllm.model_executor.layers.fused_moe.config import (
14
fp8_w8a8_moe_quant_config,
15
fp8_w8a16_moe_quant_config,
16
)
17
+from vllm.model_executor.layers.fused_moe.flashinfer_trtllm_moe import (
18
+ is_supported_config_trtllm,
19
+)
20
+from vllm.model_executor.layers.fused_moe.prepare_finalize import (
21
+ MoEPrepareAndFinalizeNoEP,
22
+)
23
from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
24
FlashinferMoeBackend,
25
get_flashinfer_moe_backend,
@@ -26,133 +32,307 @@ from vllm.model_executor.layers.quantization.utils.fp8_utils import (
32
from vllm.model_executor.layers.quantization.utils.marlin_utils_fp8 import (
33
prepare_fp8_moe_layer_for_marlin,
34
)
29
-from vllm.model_executor.layers.quantization.utils.w8a8_utils import (
30
- cutlass_group_gemm_supported,
35
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
36
+ QuantKey,
37
)
32
-from vllm.platforms import current_platform
33
-from vllm.utils.deep_gemm import is_deep_gemm_supported
34
-from vllm.utils.flashinfer import has_flashinfer_moe
35
-from vllm.utils.import_utils import has_deep_gemm
38
39
logger = init_logger(__name__)
40
41
42
class Fp8MoeBackend(Enum):
41
- NONE = 0
42
- FLASHINFER_TRTLLM = 1
43
- FLASHINFER_CUTLASS = 2
44
- DEEPGEMM = 3
45
- MARLIN = 4
46
- TRITON = 5
47
- AITER = 6
48
- VLLM_CUTLASS = 7
43
+ NONE = "NONE"
44
+ FLASHINFER_TRTLLM = "FLASHINFER_TRTLLM"
45
+ FLASHINFER_CUTLASS = "FLASHINFER_CUTLASS"
46
+ DEEPGEMM = "DEEPGEMM"
47
+ BATCHED_DEEPGEMM = "BATCHED_DEEPGEMM"
48
+ MARLIN = "MARLIN"
49
+ TRITON = "TRITON"
50
+ BATCHED_TRITON = "BATCHED_TRITON"
51
+ AITER = "AITER"
52
+ VLLM_CUTLASS = "VLLM_CUTLASS"
53
+ BATCHED_VLLM_CUTLASS = "BATCHED_VLLM_CUTLASS"
54
+
55
+
56
+def backend_to_kernel_cls(
57
+ backend: Fp8MoeBackend,
58
+) -> type[mk.FusedMoEPermuteExpertsUnpermute]:
59
+ if backend == Fp8MoeBackend.FLASHINFER_TRTLLM:
60
+ raise NotImplementedError
61
+
62
+ elif backend == Fp8MoeBackend.FLASHINFER_CUTLASS:
63
+ from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_moe import (
64
+ FlashInferExperts,
65
+ )
66
+
67
+ return FlashInferExperts
68
+
69
+ elif backend == Fp8MoeBackend.DEEPGEMM:
70
+ from vllm.model_executor.layers.fused_moe.triton_deep_gemm_moe import (
71
+ TritonOrDeepGemmExperts,
72
+ )
73
+
74
+ return TritonOrDeepGemmExperts
75
+
76
+ elif backend == Fp8MoeBackend.BATCHED_DEEPGEMM:
77
+ from vllm.model_executor.layers.fused_moe.batched_deep_gemm_moe import (
78
+ BatchedDeepGemmExperts,
79
+ )
80
+
81
+ return BatchedDeepGemmExperts
82
+
83
+ elif backend == Fp8MoeBackend.MARLIN:
84
+ from vllm.model_executor.layers.fused_moe.fused_marlin_moe import (
85
+ MarlinExperts,
86
+ )
87
+
88
+ return MarlinExperts
89
+
90
+ elif backend == Fp8MoeBackend.TRITON:
91
+ from vllm.model_executor.layers.fused_moe.fused_moe import (
92
+ TritonExperts,
93
+ )
94
+
95
+ return TritonExperts
96
+
97
+ elif backend == Fp8MoeBackend.BATCHED_TRITON:
98
+ from vllm.model_executor.layers.fused_moe.fused_batched_moe import (
99
+ BatchedTritonExperts,
100
+ )
101
+
102
+ return BatchedTritonExperts
103
+
104
+ elif backend == Fp8MoeBackend.AITER:
105
+ from vllm.model_executor.layers.fused_moe.rocm_aiter_fused_moe import (
106
+ AiterExperts,
107
+ )
108
+
109
+ return AiterExperts
110
+
111
+ elif backend == Fp8MoeBackend.VLLM_CUTLASS:
112
+ from vllm.model_executor.layers.fused_moe.triton_cutlass_moe import (
113
+ TritonOrCutlassExperts,
114
+ )
115
+
116
+ return TritonOrCutlassExperts
117
+
118
+ elif backend == Fp8MoeBackend.BATCHED_VLLM_CUTLASS:
119
+ from vllm.model_executor.layers.fused_moe.cutlass_moe import (
120
+ CutlassBatchedExpertsFp8,
121
+ )
122
+
123
+ return CutlassBatchedExpertsFp8
124
+
125
+ else:
126
+ raise ValueError(f"Unknown FP8 MoE backend: {backend.value}")
127
128
129
def select_fp8_moe_backend(
52
- block_quant: bool,
53
- tp_size: int,
54
- with_lora_support: bool,
55
- is_act_and_mul: bool,
130
+ config: FusedMoEConfig,
131
+ weight_key: QuantKey | None,
132
+ activation_key: QuantKey | None,
133
allow_vllm_cutlass: bool = False,
57
-) -> Fp8MoeBackend:
134
+) -> tuple[Fp8MoeBackend, type[mk.FusedMoEPermuteExpertsUnpermute] | None]:
135
"""
136
Select the primary FP8 MoE backend
137
Note: Shape-specific fallbacks may still occur at runtime.
138
"""
62
- # TODO(rob): in a future PR, we will query each mk for
63
- # supported features and return the mk directly, just like
64
- # we do for the Attention Backend.
139
+ k_cls: type[mk.FusedMoEPermuteExpertsUnpermute] | None = None
140
+
141
+ if config.is_lora_enabled:
142
+ return Fp8MoeBackend.TRITON, backend_to_kernel_cls(Fp8MoeBackend.TRITON)
143
+
144
+ # NOTE: the kernels are selected in the following order.
145
+ AVAILABLE_BACKENDS = [
146
+ Fp8MoeBackend.AITER,
147
+ Fp8MoeBackend.FLASHINFER_TRTLLM,
148
+ Fp8MoeBackend.FLASHINFER_CUTLASS,
149
+ Fp8MoeBackend.DEEPGEMM,
150
+ Fp8MoeBackend.BATCHED_DEEPGEMM,
151
+ Fp8MoeBackend.VLLM_CUTLASS,
152
+ Fp8MoeBackend.BATCHED_VLLM_CUTLASS,
153
+ Fp8MoeBackend.TRITON,
154
+ Fp8MoeBackend.BATCHED_TRITON,
155
+ Fp8MoeBackend.MARLIN,
156
+ ]
157
+
158
+ # NOTE(rob): We need to peak into the P/F selection to determine
159
+ # if we are using the batched or standard expert format, which
160
+ # if not ideal. Once we unify TP + DP/EP, we can select P/F first.
161
+ activation_format = (
162
+ mk.FusedMoEActivationFormat.BatchedExperts
163
+ if config.moe_parallel_config.use_batched_activation_format
164
+ else mk.FusedMoEActivationFormat.Standard
165
+ )
166
66
- if with_lora_support:
67
- return Fp8MoeBackend.TRITON
167
+ def _make_log_backend(backend: Fp8MoeBackend):
168
+ available_backend_strs = [b.value for b in AVAILABLE_BACKENDS]
169
+ return (
170
+ f"Using {backend.value} Fp8 MoE backend out "
171
+ f"of potential backends: {available_backend_strs}."
172
+ )
173
69
- def _make_log_backend(backend_name: str):
70
- return f"Using {backend_name} backend for FP8 MoE"
174
+ def _make_log_unsupported(backend: Fp8MoeBackend, reason: str | None) -> str:
175
+ if reason:
176
+ return (
177
+ f"FP8 MoE backend {backend.value} does not support the "
178
+ f"deployment configuration since {reason}."
179
+ )
180
+ else:
181
+ return (
182
+ f"FP8 MoE backend '{backend.value}' does not support the "
183
+ "deployment configuration."
184
+ )
185
72
- # Prefer FlashInfer backends on supported GPUs; allow SM90 and SM100.
73
- if (
74
- current_platform.is_cuda()
75
- and (
76
- current_platform.is_device_capability_family(100)
77
- or current_platform.is_device_capability(90)
186
+ def _return_or_raise(
187
+ backend: Fp8MoeBackend,
188
+ config: FusedMoEConfig,
189
+ weight_key: QuantKey | None,
190
+ activation_key: QuantKey | None,
191
+ activation_format: mk.FusedMoEActivationFormat,
192
+ ) -> tuple[Fp8MoeBackend, type[mk.FusedMoEPermuteExpertsUnpermute]]:
193
+ k_cls = backend_to_kernel_cls(backend)
194
+ supported, reason = k_cls.is_supported_config(
195
+ k_cls, config, weight_key, activation_key, activation_format
196
)
79
- and envs.VLLM_USE_FLASHINFER_MOE_FP8
80
- and has_flashinfer_moe()
81
- ):
82
- backend = get_flashinfer_moe_backend()
83
- if backend == FlashinferMoeBackend.TENSORRT_LLM:
84
- logger.info_once(_make_log_backend("FlashInfer TRTLLM"))
85
- if not is_act_and_mul:
86
- raise ValueError(
87
- "FlashInfer TRTLLM FP8 MoE backend only supports "
88
- "act_and_mul gate_up_project fusion. Please set "
89
- "VLLM_USE_FLASHINFER_MOE_FP8=throughput to use the "
90
- "FlashInfer CUTLASS backend instead."
197
+ if supported:
198
+ logger.info_once(_make_log_backend(backend), scope="local")
199
+ return backend, k_cls
200
+ raise ValueError(_make_log_unsupported(backend, reason))
201
+
202
+ # Handle explicit FlashInfer FP8 configuration.
203
+ if envs.is_set("VLLM_USE_FLASHINFER_MOE_FP8"):
204
+ if not envs.VLLM_USE_FLASHINFER_MOE_FP8:
205
+ # If the user rejects FlashInfer remove those backends.
206
+ AVAILABLE_BACKENDS.remove(Fp8MoeBackend.FLASHINFER_TRTLLM)
207
+ AVAILABLE_BACKENDS.remove(Fp8MoeBackend.FLASHINFER_CUTLASS)
208
+
209
+ elif envs.is_set("VLLM_FLASHINFER_MOE_BACKEND"):
210
+ # If user is explicit about backend, validate it.
211
+ fi_backend = get_flashinfer_moe_backend()
212
+
213
+ if fi_backend == FlashinferMoeBackend.TENSORRT_LLM:
214
+ backend = Fp8MoeBackend.FLASHINFER_TRTLLM
215
+ supported, reason = is_supported_config_trtllm(
216
+ config, weight_key, activation_key, activation_format
217
)
92
- return Fp8MoeBackend.FLASHINFER_TRTLLM
93
- else:
94
- if block_quant and current_platform.is_device_capability_family(100):
95
- raise ValueError(
96
- "FlashInfer FP8 MoE throughput backend does not "
97
- "support block quantization on SM100. Please use "
98
- "VLLM_FLASHINFER_MOE_BACKEND=latency to use the "
99
- "FlashInfer TRTLLM backend instead."
218
+ if supported:
219
+ logger.info_once(_make_log_backend(backend))
220
+ return backend, None
221
+ else:
222
+ raise ValueError(_make_log_unsupported(backend, reason))
223
+
224
+ elif fi_backend == FlashinferMoeBackend.CUTLASS:
225
+ backend = Fp8MoeBackend.FLASHINFER_CUTLASS
226
+ return _return_or_raise(
227
+ backend, config, weight_key, activation_key, activation_format
228
)
101
- logger.info_once(_make_log_backend("FlashInfer CUTLASS"))
102
- return Fp8MoeBackend.FLASHINFER_CUTLASS
229
104
- # weight-only path for older GPUs without native FP8
105
- if (
106
- current_platform.is_cuda() and not current_platform.has_device_capability(89)
107
- ) or envs.VLLM_TEST_FORCE_FP8_MARLIN:
108
- logger.info_once(_make_log_backend("Marlin"), scope="local")
109
- return Fp8MoeBackend.MARLIN
110
-
111
- # Determine if we should use DeepGEMM with block-quantized weights:
112
- # - If explicitly set by user, respect their choice
113
- # - If not explicitly set (default), disable when TP size is >= 8
114
- moe_use_deep_gemm = envs.VLLM_MOE_USE_DEEP_GEMM
115
- if not envs.is_set("VLLM_MOE_USE_DEEP_GEMM") and tp_size >= 8:
116
- moe_use_deep_gemm = False
117
- logger.info_once(
118
- "DeepGEMM MoE is disabled by default when TP size is >= 8. "
119
- "Set VLLM_MOE_USE_DEEP_GEMM=1 to enable it.",
120
- scope="local",
121
- )
230
+ else:
231
+ assert fi_backend == FlashinferMoeBackend.CUTEDSL
232
+ raise ValueError("FlashInfer MaskedGEMM not supported for FP8")
233
+
234
+ else:
235
+ # If the user is not explicit about the backend, try both.
236
+ for backend in [
237
+ Fp8MoeBackend.FLASHINFER_TRTLLM,
238
+ Fp8MoeBackend.FLASHINFER_CUTLASS,
239
+ ]:
240
+ if backend == Fp8MoeBackend.FLASHINFER_TRTLLM:
241
+ k_cls = None
242
+ supported, reason = is_supported_config_trtllm(
243
+ config,
244
+ weight_key,
245
+ activation_key,
246
+ activation_format,
247
+ )
248
+ else:
249
+ k_cls = backend_to_kernel_cls(backend)
250
+ supported, reason = k_cls.is_supported_config(
251
+ k_cls,
252
+ config,
253
+ weight_key,
254
+ activation_key,
255
+ activation_format,
256
+ )
257
+
258
+ if supported:
259
+ logger.info_once(_make_log_backend(backend), scope="local")
260
+ return backend, k_cls
261
+ else:
262
+ logger.debug_once(
263
+ _make_log_unsupported(backend, reason), scope="local"
264
+ )
265
+
266
+ raise NotImplementedError(
267
+ "Found VLLM_USE_FLASHINFER_MOE_FP8=1, but no "
268
+ "FlashInfer FP8 MoE backend supports the configuration."
269
+ )
270
+
271
+ # Handle explicit DeepGEMM FP8 configuration.
272
+ if envs.is_set("VLLM_USE_DEEP_GEMM") or envs.is_set("VLLM_MOE_USE_DEEP_GEMM"):
273
+ if not envs.VLLM_USE_DEEP_GEMM or not envs.VLLM_MOE_USE_DEEP_GEMM:
274
+ AVAILABLE_BACKENDS.remove(Fp8MoeBackend.DEEPGEMM)
275
+ AVAILABLE_BACKENDS.remove(Fp8MoeBackend.BATCHED_DEEPGEMM)
276
+ else:
277
+ backend = (
278
+ Fp8MoeBackend.DEEPGEMM
279
+ if activation_format == mk.FusedMoEActivationFormat.Standard
280
+ else Fp8MoeBackend.BATCHED_DEEPGEMM
281
+ )
282
+ return _return_or_raise(
283
+ backend, config, weight_key, activation_key, activation_format
284
+ )
285
123
- use_deep_gemm = envs.VLLM_USE_DEEP_GEMM
124
- if not is_deep_gemm_supported():
125
- use_deep_gemm = False
126
- logger.info_once(
127
- "DeepGEMM is disabled because the platform does not support it.",
128
- scope="local",
286
+ # Handle explicit MARLIN FP8 configuration.
287
+ if envs.VLLM_TEST_FORCE_FP8_MARLIN:
288
+ backend = Fp8MoeBackend.MARLIN
289
+ return _return_or_raise(
290
+ backend, config, weight_key, activation_key, activation_format
291
)
292
131
- if use_deep_gemm and moe_use_deep_gemm and block_quant and is_act_and_mul:
132
- if not has_deep_gemm():
133
- logger.warning_once(
134
- "DeepGEMM backend requested but not available.", scope="local"
293
+ # Handle explicit AITER FP8 configuration.
294
+ if envs.is_set("VLLM_ROCM_USE_AITER") or envs.is_set("VLLM_ROCM_USE_AITER_MOE"):
295
+ if not envs.VLLM_ROCM_USE_AITER or not envs.VLLM_ROCM_USE_AITER_MOE:
296
+ AVAILABLE_BACKENDS.remove(Fp8MoeBackend.AITER)
297
+ else:
298
+ backend = Fp8MoeBackend.AITER
299
+ return _return_or_raise(
300
+ backend, config, weight_key, activation_key, activation_format
301
)
136
- elif is_deep_gemm_supported():
137
- logger.info_once(_make_log_backend("DeepGEMM"), scope="local")
138
- return Fp8MoeBackend.DEEPGEMM
302
140
- if envs.VLLM_ROCM_USE_AITER and envs.VLLM_ROCM_USE_AITER_MOE:
141
- logger.info_once(_make_log_backend("ROCm AITER"), scope="local")
142
- return Fp8MoeBackend.AITER
303
+ if not allow_vllm_cutlass:
304
+ AVAILABLE_BACKENDS.remove(Fp8MoeBackend.VLLM_CUTLASS)
305
+ AVAILABLE_BACKENDS.remove(Fp8MoeBackend.BATCHED_VLLM_CUTLASS)
306
+
307
+ # Select kernels in order of backend.
308
+ for backend in AVAILABLE_BACKENDS:
309
+ if backend == Fp8MoeBackend.FLASHINFER_TRTLLM:
310
+ k_cls = None
311
+ supported, reason = is_supported_config_trtllm(
312
+ config,
313
+ weight_key,
314
+ activation_key,
315
+ activation_format,
316
+ )
317
+ else:
318
+ k_cls = backend_to_kernel_cls(backend)
319
+ supported, reason = k_cls.is_supported_config(
320
+ k_cls,
321
+ config,
322
+ weight_key,
323
+ activation_key,
324
+ activation_format,
325
+ )
326
144
- if (
145
- allow_vllm_cutlass
146
- and not block_quant
147
- and cutlass_group_gemm_supported()
148
- and is_act_and_mul
149
- ):
150
- logger.info_once(_make_log_backend("vLLM CUTLASS"), scope="local")
151
- return Fp8MoeBackend.VLLM_CUTLASS
327
+ if supported:
328
+ logger.info_once(_make_log_backend(backend), scope="local")
329
+ return backend, k_cls
330
+ else:
331
+ logger.debug_once(_make_log_unsupported(backend, reason), scope="local")
332
153
- # default to Triton
154
- logger.info_once(_make_log_backend("Triton"), scope="local")
155
- return Fp8MoeBackend.TRITON
333
+ raise NotImplementedError(
334
+ "No FP8 MoE backend supports the deployment configuration."
335
+ )
336
337
338
def convert_to_fp8_moe_kernel_format(
@@ -166,7 +346,7 @@ def convert_to_fp8_moe_kernel_format(
346
w2_input_scale: torch.Tensor | None,
347
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
348
block_quant = hasattr(layer, "weight_block_size")
169
- if fp8_backend == Fp8MoeBackend.DEEPGEMM:
349
+ if fp8_backend in [Fp8MoeBackend.DEEPGEMM, Fp8MoeBackend.BATCHED_DEEPGEMM]:
350
assert block_quant
351
w13, w2, w13_scale, w2_scale = prepare_fp8_moe_layer_for_deepgemm(
352
w13,
@@ -199,6 +379,14 @@ def convert_to_fp8_moe_kernel_format(
379
w2_input_scale=w2_input_scale,
380
is_trtllm=(fp8_backend == Fp8MoeBackend.FLASHINFER_TRTLLM),
381
)
382
+ else:
383
+ if fp8_backend not in [
384
+ Fp8MoeBackend.TRITON,
385
+ Fp8MoeBackend.BATCHED_TRITON,
386
+ Fp8MoeBackend.VLLM_CUTLASS,
387
+ Fp8MoeBackend.BATCHED_VLLM_CUTLASS,
388
+ ]:
389
+ raise ValueError(f"Unsupported FP8 MoE backend: {fp8_backend.value}")
390
391
return w13, w2, w13_scale, w2_scale
392
@@ -210,6 +398,8 @@ def make_fp8_moe_quant_config(
398
a1_scale: torch.Tensor | None,
399
a2_scale: torch.Tensor | None,
400
block_shape: list[int] | None = None,
401
+ per_act_token_quant: bool = False,
402
+ per_out_ch_quant: bool = False,
403
) -> FusedMoEQuantConfig | None:
404
"""
405
Create FusedMoEQuantConfig for the specifed FP8 Backend.
@@ -262,102 +452,76 @@ def make_fp8_moe_quant_config(
452
a1_scale=a1_scale,
453
a2_scale=a2_scale,
454
block_shape=block_shape,
455
+ per_act_token_quant=per_act_token_quant,
456
+ per_out_ch_quant=per_out_ch_quant,
457
)
458
459
268
-def make_fp8_moe_kernel(
269
- layer: torch.nn.Module,
270
- moe_quant_config: FusedMoEQuantConfig,
460
+def make_fp8_moe_kernel_for_mkm(
461
moe_config: FusedMoEConfig,
272
- fp8_backend: Fp8MoeBackend,
273
-) -> tuple[mk.FusedMoEModularKernel, bool]:
274
- # Delayed import is required since the oracle is imported
275
- # by CPU backends which cannot import all of these experts.
276
- # TODO: update the experts to make this not happen.
277
- from vllm.model_executor.layers.fused_moe.prepare_finalize import (
278
- MoEPrepareAndFinalizeNoEP,
279
- )
280
-
281
- # NOTE(rob): this is a WIP refactor. We are first migrating
282
- # all of the kernels in the TP case to use mk. Once this is
283
- # done, then we will initialzie the TP case and DP/EP case
284
- # via the same code path (i.e. via maybe_init_modular_kernel).
285
- # NOTE(rob): in progress migrating all into this format.
286
- use_inplace = True
287
- if fp8_backend == Fp8MoeBackend.FLASHINFER_CUTLASS:
288
- from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_moe import (
289
- FlashInferExperts,
462
+ quant_config: FusedMoEQuantConfig,
463
+ experts_cls: type[mk.FusedMoEPermuteExpertsUnpermute],
464
+ prepare_finalize: mk.FusedMoEPrepareAndFinalize,
465
+) -> mk.FusedMoEPermuteExpertsUnpermute:
466
+ if prepare_finalize.activation_format == mk.FusedMoEActivationFormat.BatchedExperts:
467
+ max_num_tokens_per_rank = prepare_finalize.max_num_tokens_per_rank()
468
+ assert max_num_tokens_per_rank is not None
469
+ experts = experts_cls(
470
+ moe_config=moe_config,
471
+ quant_config=quant_config,
472
+ max_num_tokens=max_num_tokens_per_rank,
473
+ num_dispatchers=prepare_finalize.num_dispatchers(),
474
)
291
-
292
- kernel = mk.FusedMoEModularKernel(
293
- MoEPrepareAndFinalizeNoEP(
294
- defer_input_quant=moe_quant_config.is_block_quantized
295
- ),
296
- FlashInferExperts(
297
- out_dtype=layer.orig_dtype,
298
- quant_config=moe_quant_config,
299
- ep_rank=moe_config.ep_rank,
300
- ep_size=moe_config.ep_size,
301
- tp_rank=moe_config.tp_rank,
302
- tp_size=moe_config.tp_size,
303
- use_dp=(moe_config.dp_size > 1),
304
- use_deepseek_fp8_block_scale=moe_quant_config.is_block_quantized,
305
- ),
475
+ else:
476
+ experts = experts_cls(
477
+ moe_config=moe_config,
478
+ quant_config=quant_config,
479
)
307
- use_inplace = False
480
309
- elif fp8_backend == Fp8MoeBackend.AITER:
310
- from vllm.model_executor.layers.fused_moe.rocm_aiter_fused_moe import (
311
- AiterExperts,
312
- )
481
+ logger.debug_once("Using %s", experts.__class__.__name__)
482
+ return experts
483
314
- kernel = mk.FusedMoEModularKernel(
315
- # TODO: make defer_input_quant an attr of the AiterExperts
316
- MoEPrepareAndFinalizeNoEP(defer_input_quant=True),
317
- AiterExperts(quant_config=moe_quant_config),
318
- )
319
- elif fp8_backend == Fp8MoeBackend.MARLIN:
320
- from vllm.model_executor.layers.fused_moe.fused_marlin_moe import (
321
- MarlinExperts,
322
- )
484
324
- kernel = mk.FusedMoEModularKernel(
325
- MoEPrepareAndFinalizeNoEP(),
326
- MarlinExperts(quant_config=moe_quant_config),
327
- )
328
- elif fp8_backend == Fp8MoeBackend.VLLM_CUTLASS:
329
- from vllm.model_executor.layers.fused_moe.triton_cutlass_moe import (
330
- TritonOrCutlassExperts,
485
+def make_fp8_moe_kernel(
486
+ moe_quant_config: FusedMoEQuantConfig,
487
+ moe_config: FusedMoEConfig,
488
+ fp8_backend: Fp8MoeBackend,
489
+ experts_cls: type[mk.FusedMoEPermuteExpertsUnpermute],
490
+) -> tuple[mk.FusedMoEModularKernel, bool]:
491
+ # TODO(rob): unify after we merge tp and dp/ep.
492
+ if (
493
+ moe_config.moe_parallel_config.use_all2all_kernels
494
+ and moe_config.moe_parallel_config.all2all_backend
495
+ not in ["allgather_reducescatter", "naive"]
496
+ ):
497
+ raise ValueError(
498
+ "Fp8 Oracle should not create non-naive A2A P/F. "
499
+ "This should happen via the ModularKernelMethod."
500
)
501
333
- kernel = mk.FusedMoEModularKernel(
334
- MoEPrepareAndFinalizeNoEP(),
335
- TritonOrCutlassExperts(
336
- out_dtype=moe_config.in_dtype,
337
- e=layer.local_num_experts,
338
- n=layer.intermediate_size_per_partition,
339
- k=layer.hidden_size,
340
- device=layer.w13_weight.device,
341
- quant_config=moe_quant_config,
342
- ),
343
- )
344
- elif fp8_backend == Fp8MoeBackend.DEEPGEMM:
345
- from vllm.model_executor.layers.fused_moe import (
346
- TritonOrDeepGemmExperts,
347
- )
502
+ # Create Prepare/Finalize.
503
+ prepare_finalize = MoEPrepareAndFinalizeNoEP(
504
+ defer_input_quant=experts_cls.expects_unquantized_inputs(
505
+ moe_config, moe_quant_config
506
+ ),
507
+ )
508
349
- kernel = mk.FusedMoEModularKernel(
350
- MoEPrepareAndFinalizeNoEP(),
351
- TritonOrDeepGemmExperts(quant_config=moe_quant_config),
352
- )
353
- else:
354
- from vllm.model_executor.layers.fused_moe.fused_moe import (
355
- TritonExperts,
356
- )
509
+ # Create Experts.
510
+ experts = experts_cls(
511
+ moe_config=moe_config,
512
+ quant_config=moe_quant_config,
513
+ )
514
358
- assert fp8_backend == Fp8MoeBackend.TRITON
359
- kernel = mk.FusedMoEModularKernel(
360
- MoEPrepareAndFinalizeNoEP(),
361
- TritonExperts(quant_config=moe_quant_config),
362
- )
363
- return kernel, use_inplace
515
+ # NOTE(rob): we only want the mk to control the shared_expert
516
+ # if using all2all (for SBO). bnell is making this explict in
517
+ # the new MoE runner class.
518
+ kernel = mk.FusedMoEModularKernel(
519
+ prepare_finalize,
520
+ experts,
521
+ shared_experts=None,
522
+ moe_parallel_config=moe_config.moe_parallel_config,
523
+ )
524
+
525
+ # TODO(rob): update inplace logic to be part of the kernel.
526
+ inplace = fp8_backend != Fp8MoeBackend.FLASHINFER_CUTLASS
527
+ return kernel, inplace
vllm/model_executor/layers/fused_moe/oracle/nvfp4.py
+258
-91
@@ -14,21 +14,11 @@ from vllm.model_executor.layers.fused_moe.config import (
14
nvfp4_moe_quant_config,
15
nvfp4_w4a16_moe_quant_config,
16
)
17
-from vllm.model_executor.layers.fused_moe.cutlass_moe import (
18
- CutlassExpertsFp4,
19
-)
20
-from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_moe import (
21
- FlashInferExperts,
22
-)
23
-from vllm.model_executor.layers.fused_moe.fused_marlin_moe import (
24
- MarlinExperts,
25
-)
17
from vllm.model_executor.layers.fused_moe.prepare_finalize import (
18
MoEPrepareAndFinalizeNoEP,
19
)
20
from vllm.model_executor.layers.quantization.utils.flashinfer_fp4_moe import (
30
- is_flashinfer_fp4_cutedsl_moe_available,
31
- is_flashinfer_fp4_cutlass_moe_available,
21
+ is_supported_config_trtllm,
22
prepare_nvfp4_moe_layer_for_fi_or_cutlass,
23
)
24
from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
@@ -36,27 +26,26 @@ from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
26
get_flashinfer_moe_backend,
27
)
28
from vllm.model_executor.layers.quantization.utils.marlin_utils_fp4 import (
39
- is_fp4_marlin_supported,
29
prepare_nvfp4_moe_layer_for_marlin,
30
)
31
from vllm.model_executor.layers.quantization.utils.quant_utils import (
43
- cutlass_fp4_supported,
32
+ QuantKey,
33
)
34
35
logger = init_logger(__name__)
36
37
38
class NvFp4MoeBackend(Enum):
50
- FLASHINFER_CUTLASS = "FlashInfer CUTLASS"
51
- FLASHINFER_TRTLLM = "FlashInfer TRTLLM"
52
- FLASHINFER_CUTEDSL = "FlashInfer CUTEDSL"
53
- VLLM_CUTLASS = "vLLM CUTASS"
54
- MARLIN = "vLLM MARLIN"
39
+ FLASHINFER_TRTLLM = "FLASHINFER_TRTLLM"
40
+ FLASHINFER_CUTLASS = "FLASHINFER_CUTLASS"
41
+ FLASHINFER_CUTEDSL = "FLASHINFER_CUTEDSL"
42
+ VLLM_CUTLASS = "VLLM_CUTLASS"
43
+ MARLIN = "MARLIN"
44
45
46
FLASHINFER_NVFP4_MOE_BACKENDS = [
58
- NvFp4MoeBackend.FLASHINFER_CUTLASS,
47
NvFp4MoeBackend.FLASHINFER_TRTLLM,
48
+ NvFp4MoeBackend.FLASHINFER_CUTLASS,
49
NvFp4MoeBackend.FLASHINFER_CUTEDSL,
50
]
51
@@ -72,44 +61,208 @@ def is_global_sf_supported_for_nvfp4_backend(backend: NvFp4MoeBackend) -> bool:
61
# of all experts in Expert Parallel Mode when all experts are not
62
# on the same rank.
63
75
- return backend in [
76
- NvFp4MoeBackend.FLASHINFER_CUTLASS,
64
+ return backend in FLASHINFER_NVFP4_MOE_BACKENDS
65
+
66
+
67
+def backend_to_kernel_cls(
68
+ backend: NvFp4MoeBackend,
69
+) -> type[mk.FusedMoEPermuteExpertsUnpermute]:
70
+ if backend == NvFp4MoeBackend.FLASHINFER_TRTLLM:
71
+ raise NotImplementedError(
72
+ "FLASHINFER_TRTLLM doesn't support Modular Kernel Interface"
73
+ )
74
+
75
+ elif backend == NvFp4MoeBackend.FLASHINFER_CUTLASS:
76
+ from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_moe import (
77
+ FlashInferExperts,
78
+ )
79
+
80
+ return FlashInferExperts
81
+
82
+ elif backend == NvFp4MoeBackend.FLASHINFER_CUTEDSL:
83
+ from vllm.model_executor.layers.fused_moe.flashinfer_cutedsl_moe import (
84
+ FlashInferCuteDSLExperts,
85
+ )
86
+
87
+ return FlashInferCuteDSLExperts
88
+
89
+ elif backend == NvFp4MoeBackend.VLLM_CUTLASS:
90
+ from vllm.model_executor.layers.fused_moe.cutlass_moe import (
91
+ CutlassExpertsFp4,
92
+ )
93
+
94
+ return CutlassExpertsFp4
95
+
96
+ elif backend == NvFp4MoeBackend.MARLIN:
97
+ from vllm.model_executor.layers.fused_moe.fused_marlin_moe import (
98
+ MarlinExperts,
99
+ )
100
+
101
+ return MarlinExperts
102
+ else:
103
+ raise ValueError(f"Unknown NvFP4 MoE backend: {backend.value}")
104
+
105
+
106
+def select_nvfp4_moe_backend(
107
+ config: FusedMoEConfig,
108
+ weight_key: QuantKey | None,
109
+ activation_key: QuantKey | None,
110
+) -> tuple[NvFp4MoeBackend, type[mk.FusedMoEPermuteExpertsUnpermute] | None]:
111
+ """
112
+ Select the primary NvFP4 MoE backend
113
+ Note: Shape-specific fallbacks may still occur at runtime.
114
+ """
115
+
116
+ # NOTE: the kernels are selected in the following order.
117
+ AVAILABLE_BACKENDS = [
118
NvFp4MoeBackend.FLASHINFER_TRTLLM,
119
+ NvFp4MoeBackend.FLASHINFER_CUTEDSL,
120
+ NvFp4MoeBackend.FLASHINFER_CUTLASS,
121
+ NvFp4MoeBackend.VLLM_CUTLASS,
122
+ NvFp4MoeBackend.MARLIN,
123
]
124
125
+ # NOTE(rob): this is kind of a hack. We need to peak into
126
+ # the prepare-finalize selection to determine if we are using
127
+ # the batched or standard expert format.
128
+ use_batched = (
129
+ config.moe_parallel_config.use_deepep_ll_kernels
130
+ or config.moe_parallel_config.use_pplx_kernels
131
+ )
132
+ activation_format = (
133
+ mk.FusedMoEActivationFormat.BatchedExperts
134
+ if use_batched
135
+ else mk.FusedMoEActivationFormat.Standard
136
+ )
137
81
-def select_nvfp4_moe_backend() -> NvFp4MoeBackend:
138
def _make_log_backend(backend: NvFp4MoeBackend):
83
- return f"Using {backend.value} backend for NvFp4 MoE"
139
+ available_backend_strs = [b.value for b in AVAILABLE_BACKENDS]
140
+ return (
141
+ f"Using '{backend.value}' NvFp4 MoE backend out "
142
+ f"of potential backends: {available_backend_strs}."
143
+ )
144
85
- if cutlass_fp4_supported() and not envs.VLLM_TEST_FORCE_FP8_MARLIN:
86
- allow_flashinfer = (
87
- is_flashinfer_fp4_cutlass_moe_available()
88
- or is_flashinfer_fp4_cutedsl_moe_available()
145
+ def _make_log_unsupported(backend: NvFp4MoeBackend, reason: str | None) -> str:
146
+ if reason:
147
+ return (
148
+ f"NvFp4 MoE backend '{backend.value}' does not support the "
149
+ f"deployment configuration since {reason}."
150
+ )
151
+ else:
152
+ return (
153
+ f"NvFp4 MoE backend '{backend.value}' does not support the "
154
+ "deployment configuration."
155
+ )
156
+
157
+ def _return_or_raise(
158
+ backend: NvFp4MoeBackend,
159
+ config: FusedMoEConfig,
160
+ weight_key: QuantKey | None,
161
+ activation_key: QuantKey | None,
162
+ activation_format: mk.FusedMoEActivationFormat,
163
+ ) -> tuple[NvFp4MoeBackend, type[mk.FusedMoEPermuteExpertsUnpermute]]:
164
+ k_cls = backend_to_kernel_cls(backend)
165
+ supported, reason = k_cls.is_supported_config(
166
+ k_cls, config, weight_key, activation_key, activation_format
167
)
90
- if allow_flashinfer and envs.VLLM_USE_FLASHINFER_MOE_FP4:
91
- backend = fi_2_vllm_backend_map[get_flashinfer_moe_backend()]
168
+ if supported:
169
+ logger.info_once(_make_log_backend(backend))
170
+ return backend, k_cls
171
+ raise ValueError(_make_log_unsupported(backend, reason))
172
+
173
+ if envs.is_set("VLLM_USE_FLASHINFER_MOE_FP4"):
174
+ if not envs.VLLM_USE_FLASHINFER_MOE_FP4:
175
+ # If the user rejects FlashInfer remove those backends.
176
+ for b in FLASHINFER_NVFP4_MOE_BACKENDS:
177
+ AVAILABLE_BACKENDS.remove(b)
178
+
179
+ elif envs.is_set("VLLM_FLASHINFER_MOE_BACKEND"):
180
+ # If user is explicit about backend, validate it.
181
+ fi_backend = get_flashinfer_moe_backend()
182
+
183
+ if fi_backend == FlashinferMoeBackend.TENSORRT_LLM:
184
+ backend = NvFp4MoeBackend.FLASHINFER_TRTLLM
185
+ supported, reason = is_supported_config_trtllm(
186
+ config, weight_key, activation_key, activation_format
187
+ )
188
+ if supported:
189
+ logger.info_once(_make_log_backend(backend))
190
+ return backend, None
191
+ else:
192
+ raise ValueError(_make_log_unsupported(backend, reason))
193
+ else:
194
+ backend = fi_2_vllm_backend_map[fi_backend]
195
+ return _return_or_raise(
196
+ backend, config, weight_key, activation_key, activation_format
197
+ )
198
else:
93
- backend = NvFp4MoeBackend.VLLM_CUTLASS
94
- elif is_fp4_marlin_supported():
199
+ # If the user is not explicit about the backend, try each.
200
+ for backend in FLASHINFER_NVFP4_MOE_BACKENDS:
201
+ if backend == NvFp4MoeBackend.FLASHINFER_TRTLLM:
202
+ k_cls = None
203
+ supported, reason = is_supported_config_trtllm(
204
+ config,
205
+ weight_key,
206
+ activation_key,
207
+ activation_format,
208
+ )
209
+ else:
210
+ k_cls = backend_to_kernel_cls(backend)
211
+ supported, reason = k_cls.is_supported_config(
212
+ k_cls,
213
+ config,
214
+ weight_key,
215
+ activation_key,
216
+ activation_format,
217
+ )
218
+ if supported:
219
+ logger.info_once(_make_log_backend(backend), scope="local")
220
+ return backend, None
221
+ else:
222
+ logger.debug_once(
223
+ _make_log_unsupported(backend, reason), scope="local"
224
+ )
225
+
226
+ raise NotImplementedError(
227
+ "Found VLLM_USE_FLASHINFER_MOE_FP4=1, but no "
228
+ "FlashInfer NVFP4 MoE backend supports the configuration."
229
+ )
230
+
231
+ if envs.VLLM_TEST_FORCE_FP8_MARLIN:
232
backend = NvFp4MoeBackend.MARLIN
96
- else:
97
- raise ValueError("No NvFp4 kernel backend available for NvFp4 MoE.")
98
-
99
- # Log warning if FI backend requested but not available.
100
- if (
101
- backend not in FLASHINFER_NVFP4_MOE_BACKENDS
102
- and envs.VLLM_USE_FLASHINFER_MOE_FP4
103
- ):
104
- logger.warning_once(
105
- "Requested FlashInfer backend for NvFp4 MoE, but it's not available. "
106
- "Falling back to %s for NvFp4 MoE",
107
- backend.value,
108
- scope="local",
233
+ return _return_or_raise(
234
+ backend, config, weight_key, activation_key, activation_format
235
)
110
- else:
111
- logger.info_once(_make_log_backend(backend), scope="local")
112
- return backend
236
+
237
+ # Select kernels in order of backend.
238
+ for backend in AVAILABLE_BACKENDS:
239
+ if backend == NvFp4MoeBackend.FLASHINFER_TRTLLM:
240
+ k_cls = None # type: ignore[assignment]
241
+ supported, reason = is_supported_config_trtllm(
242
+ config,
243
+ weight_key,
244
+ activation_key,
245
+ activation_format,
246
+ )
247
+ else:
248
+ k_cls = backend_to_kernel_cls(backend)
249
+ supported, reason = k_cls.is_supported_config(
250
+ k_cls,
251
+ config,
252
+ weight_key,
253
+ activation_key,
254
+ activation_format,
255
+ )
256
+
257
+ if supported:
258
+ logger.info_once(_make_log_backend(backend), scope="local")
259
+ return backend, k_cls
260
+ else:
261
+ logger.debug_once(_make_log_unsupported(backend, reason), scope="local")
262
+
263
+ raise NotImplementedError(
264
+ "No NvFp4 MoE backend supports the deployment configuration."
265
+ )
266
267
268
def convert_to_nvfp4_moe_kernel_format(
@@ -238,55 +391,69 @@ def make_nvfp4_moe_quant_config(
391
)
392
393
241
-def make_nvfp4_moe_kernel(
242
- backend: NvFp4MoeBackend,
243
- quant_config: FusedMoEQuantConfig,
394
+def make_nvfp4_moe_kernel_for_mkm(
395
moe_config: FusedMoEConfig,
245
-) -> mk.FusedMoEModularKernel | None:
246
- assert moe_config.dp_size == 1
396
+ quant_config: FusedMoEQuantConfig,
397
+ experts_cls: type[mk.FusedMoEPermuteExpertsUnpermute],
398
+ prepare_finalize: mk.FusedMoEPrepareAndFinalize,
399
+) -> mk.FusedMoEPermuteExpertsUnpermute:
400
+ if prepare_finalize.activation_format == mk.FusedMoEActivationFormat.BatchedExperts:
401
+ max_num_tokens_per_rank = prepare_finalize.max_num_tokens_per_rank()
402
+ assert max_num_tokens_per_rank is not None
403
+ experts = experts_cls(
404
+ moe_config=moe_config,
405
+ quant_config=quant_config,
406
+ max_num_tokens=max_num_tokens_per_rank,
407
+ num_dispatchers=prepare_finalize.num_dispatchers(),
408
+ )
409
+ else:
410
+ experts = experts_cls(
411
+ moe_config=moe_config,
412
+ quant_config=quant_config,
413
+ )
414
248
- UNSUPPORTED_BACKENDS = [
249
- # TRTLLM does not use the modular kernl abstraction.
250
- NvFp4MoeBackend.FLASHINFER_TRTLLM,
251
- # CUTEDSL is used with BATCHED (masked) format only.
252
- # TODO: add here once we support dp/ep via the oracle.
253
- NvFp4MoeBackend.FLASHINFER_CUTEDSL,
254
- ]
415
+ logger.debug_once("Using %s", experts.__class__.__name__)
416
+ return experts
417
256
- if backend in UNSUPPORTED_BACKENDS:
257
- return None
418
259
- elif backend == NvFp4MoeBackend.FLASHINFER_CUTLASS:
260
- return mk.FusedMoEModularKernel(
261
- MoEPrepareAndFinalizeNoEP(defer_input_quant=True),
262
- FlashInferExperts(
263
- out_dtype=moe_config.in_dtype,
264
- quant_config=quant_config,
265
- ep_rank=moe_config.ep_rank,
266
- ep_size=moe_config.ep_size,
267
- tp_rank=moe_config.tp_rank,
268
- tp_size=moe_config.tp_size,
269
- use_dp=False,
270
- use_deepseek_fp8_block_scale=False,
271
- ),
419
+def make_nvfp4_moe_kernel(
420
+ moe_quant_config: FusedMoEQuantConfig,
421
+ moe_config: FusedMoEConfig,
422
+ experts_cls: type[mk.FusedMoEPermuteExpertsUnpermute],
423
+) -> mk.FusedMoEModularKernel:
424
+ # TODO(rob): unify after we merge tp and dp/ep.
425
+ if (
426
+ moe_config.moe_parallel_config.use_all2all_kernels
427
+ and moe_config.moe_parallel_config.all2all_backend
428
+ not in ["allgather_reducescatter", "naive"]
429
+ ):
430
+ raise ValueError(
431
+ "NvFP4 Oracle should not create non-naive A2A P/F. "
432
+ "This should happen via the ModularKernelMethod."
433
)
434
274
- elif backend == NvFp4MoeBackend.VLLM_CUTLASS:
275
- return mk.FusedMoEModularKernel(
276
- MoEPrepareAndFinalizeNoEP(defer_input_quant=True),
277
- CutlassExpertsFp4(
278
- out_dtype=moe_config.in_dtype,
279
- # TODO(rob): see what impact this has on expert map?
280
- max_experts_per_worker=moe_config.num_experts,
281
- quant_config=quant_config,
282
- ),
283
- )
435
+ # Create Prepare/Finalize.
436
+ prepare_finalize = MoEPrepareAndFinalizeNoEP(
437
+ defer_input_quant=experts_cls.expects_unquantized_inputs(
438
+ moe_config, moe_quant_config
439
+ ),
440
+ )
441
285
- elif backend == NvFp4MoeBackend.MARLIN:
286
- return mk.FusedMoEModularKernel(
287
- MoEPrepareAndFinalizeNoEP(),
288
- MarlinExperts(quant_config=quant_config),
289
- )
442
+ # Create Experts.
443
+ experts = experts_cls(
444
+ moe_config=moe_config,
445
+ quant_config=moe_quant_config,
446
+ )
447
291
- else:
292
- raise ValueError(f"Unknown NvFp4 MoE backend: {backend}")
448
+ # NOTE(rob): we only want the mk to control the shared_expert
449
+ # if using all2all (for SBO). bnell is making this explict in
450
+ # the new MoE runner class.
451
+ kernel = mk.FusedMoEModularKernel(
452
+ prepare_finalize,
453
+ experts,
454
+ shared_experts=None,
455
+ moe_parallel_config=moe_config.moe_parallel_config,
456
+ )
457
+
458
+ # TODO(rob): update inplace logic to be part of the kernel.
459
+ return kernel
vllm/model_executor/layers/fused_moe/oracle/unquantized.py
+9
-8
@@ -123,7 +123,6 @@ def convert_to_unquantized_kernel_format(
123
124
125
def make_unquantized_moe_kernel(
126
- layer: torch.nn.Module,
126
backend: UnquantizedMoeBackend,
127
quant_config: FusedMoEQuantConfig,
128
moe_config: FusedMoEConfig,
@@ -141,12 +140,8 @@ def make_unquantized_moe_kernel(
140
kernel = mk.FusedMoEModularKernel(
141
MoEPrepareAndFinalizeNoEP(),
142
FlashInferExperts(
144
- out_dtype=layer.params_dtype,
143
+ moe_config=moe_config,
144
quant_config=quant_config,
146
- tp_rank=moe_config.moe_parallel_config.tp_rank,
147
- tp_size=moe_config.moe_parallel_config.tp_size,
148
- ep_rank=moe_config.moe_parallel_config.ep_rank,
149
- ep_size=moe_config.moe_parallel_config.ep_size,
145
),
146
)
147
use_inplace = False
@@ -157,13 +152,19 @@ def make_unquantized_moe_kernel(
152
153
kernel = mk.FusedMoEModularKernel(
154
MoEPrepareAndFinalizeNoEP(),
160
- AiterExperts(quant_config),
155
+ AiterExperts(
156
+ moe_config=moe_config,
157
+ quant_config=quant_config,
158
+ ),
159
)
160
elif backend == UnquantizedMoeBackend.TRITON:
161
from vllm.model_executor.layers.fused_moe import TritonExperts
162
163
kernel = mk.FusedMoEModularKernel(
164
MoEPrepareAndFinalizeNoEP(),
167
- TritonExperts(quant_config),
165
+ TritonExperts(
166
+ moe_config=moe_config,
167
+ quant_config=quant_config,
168
+ ),
169
)
170
return kernel, use_inplace
vllm/model_executor/layers/fused_moe/rocm_aiter_fused_moe.py
+52
-10
@@ -9,11 +9,21 @@ import vllm.model_executor.layers.fused_moe.modular_kernel as mk
9
from vllm._aiter_ops import rocm_aiter_ops
10
from vllm.model_executor.layers.fused_moe.config import (
11
FUSED_MOE_UNQUANTIZED_CONFIG,
12
+ FusedMoEParallelConfig,
13
FusedMoEQuantConfig,
14
)
15
from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
16
TopKWeightAndReduceNoOP,
17
)
18
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
19
+ QuantKey,
20
+ kFp8Dynamic128Sym,
21
+ kFp8DynamicTensorSym,
22
+ kFp8DynamicTokenSym,
23
+ kFp8Static128BlockSym,
24
+ kFp8StaticChannelSym,
25
+ kFp8StaticTensorSym,
26
+)
27
28
29
class QuantMethod(IntEnum):
@@ -269,17 +279,49 @@ def rocm_aiter_fused_experts(
279
280
281
class AiterExperts(mk.FusedMoEPermuteExpertsUnpermute):
272
- def __init__(self, quant_config):
273
- super().__init__(quant_config)
282
+ @staticmethod
283
+ def activation_format() -> mk.FusedMoEActivationFormat:
284
+ return mk.FusedMoEActivationFormat.Standard
285
+
286
+ @staticmethod
287
+ def expects_unquantized_inputs(
288
+ fused_moe_config: mk.FusedMoEConfig, quant_config: FusedMoEQuantConfig
289
+ ) -> bool:
290
+ # AITER fused MoE kernels handle input quantization internally.
291
+ return True
292
275
- @property
276
- def activation_formats(
277
- self,
278
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
279
- return (
280
- mk.FusedMoEActivationFormat.Standard,
281
- mk.FusedMoEActivationFormat.Standard,
282
- )
293
+ @staticmethod
294
+ def _supports_current_device() -> bool:
295
+ return rocm_aiter_ops.is_fused_moe_enabled()
296
+
297
+ @staticmethod
298
+ def _supports_no_act_and_mul() -> bool:
299
+ return False
300
+
301
+ @staticmethod
302
+ def _supports_quant_scheme(
303
+ weight_key: QuantKey | None,
304
+ activation_key: QuantKey | None,
305
+ ) -> bool:
306
+ # TODO(rob): AITER also supports MXFP4, which is not
307
+ # yet supported via an Oracle. Once it is, we will add
308
+ # MXFP4 to this list.
309
+ SUPPORTED_W_A = [
310
+ (None, None),
311
+ (kFp8Static128BlockSym, kFp8Dynamic128Sym),
312
+ (kFp8StaticTensorSym, kFp8StaticTensorSym),
313
+ (kFp8StaticTensorSym, kFp8DynamicTensorSym),
314
+ (kFp8StaticChannelSym, kFp8DynamicTokenSym),
315
+ ]
316
+ return (weight_key, activation_key) in SUPPORTED_W_A
317
+
318
+ @staticmethod
319
+ def _supports_activation(activation: str) -> bool:
320
+ return activation in ["silu", "gelu"]
321
+
322
+ @staticmethod
323
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
324
+ return True
325
326
def supports_expert_map(self):
327
return True
vllm/model_executor/layers/fused_moe/router/custom_routing_router.py
+5
@@ -34,6 +34,11 @@ class CustomRoutingRouter(BaseRouter):
34
35
@property
36
def routing_method_type(self) -> RoutingMethodType:
37
+ from vllm.model_executor.models.llama4 import Llama4MoE
38
+
39
+ # NOTE: FLASHINFER_TRTLLM support the Llama4 router.
40
+ if self.custom_routing_function == Llama4MoE.custom_routing_function:
41
+ return RoutingMethodType.Llama4
42
return RoutingMethodType.Custom
43
44
def _compute_routing(
vllm/model_executor/layers/fused_moe/router/grouped_topk_router.py
+4
-6
@@ -261,7 +261,6 @@ class GroupedTopKRouter(BaseRouter):
261
num_fused_shared_experts: int = 0,
262
enable_eplb: bool = False,
263
indices_type_getter: Callable[[], torch.dtype | None] | None = None,
264
- routing_method_type: RoutingMethodType | None = None,
264
):
265
super().__init__(
266
top_k=top_k,
@@ -278,13 +277,12 @@ class GroupedTopKRouter(BaseRouter):
277
self.e_score_correction_bias = e_score_correction_bias
278
self.num_fused_shared_experts = num_fused_shared_experts
279
281
- # Determine routing method type
282
- if routing_method_type is not None:
283
- self._routing_method_type = routing_method_type
284
- elif scoring_func == "sigmoid":
280
+ if scoring_func == "sigmoid":
281
self._routing_method_type = RoutingMethodType.DeepSeekV3
282
else:
287
- self._routing_method_type = RoutingMethodType.TopK
283
+ # NOTE: this prohibits the FLASHINFER_TRTLLM kernels from
284
+ # being selected, since they only support DeepSeek-style.
285
+ self._routing_method_type = RoutingMethodType.Unspecified
286
287
@property
288
def routing_method_type(self) -> RoutingMethodType:
vllm/model_executor/layers/fused_moe/router/router_factory.py
-3
@@ -6,7 +6,6 @@ import torch
6
7
import vllm.envs as envs
8
from vllm.distributed.eplb.eplb_state import EplbLayerState
9
-from vllm.model_executor.layers.fused_moe.config import RoutingMethodType
9
from vllm.model_executor.layers.fused_moe.router.base_router import BaseRouter
10
from vllm.model_executor.layers.fused_moe.router.custom_routing_router import (
11
CustomRoutingRouter,
@@ -36,7 +35,6 @@ def create_fused_moe_router(
35
global_num_experts: int,
36
renormalize: bool = True,
37
indices_type_getter: Callable[[], torch.dtype | None] | None = None,
39
- routing_method_type: RoutingMethodType | None = None,
38
# grouped topk parameters
39
use_grouped_topk: bool = False,
40
num_expert_group: int | None = None,
@@ -128,7 +126,6 @@ def create_fused_moe_router(
126
num_fused_shared_experts=num_fused_shared_experts,
127
enable_eplb=enable_eplb,
128
indices_type_getter=indices_type_getter,
131
- routing_method_type=routing_method_type,
129
)
130
router.capture = capture
131
return router
vllm/model_executor/layers/fused_moe/triton_cutlass_moe.py
+14
-8
@@ -5,7 +5,10 @@
5
import torch
6
7
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8
-from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
8
+from vllm.model_executor.layers.fused_moe.config import (
9
+ FusedMoEConfig,
10
+ FusedMoEQuantConfig,
11
+)
12
from vllm.model_executor.layers.fused_moe.cutlass_moe import CutlassExpertsFp8
13
from vllm.model_executor.layers.fused_moe.fallback import FallbackExperts
14
from vllm.model_executor.layers.fused_moe.fused_moe import TritonExperts
@@ -17,19 +20,22 @@ class TritonOrCutlassExperts(FallbackExperts):
20
21
def __init__(
22
self,
20
- e: int,
21
- n: int,
22
- k: int,
23
- out_dtype: torch.dtype | None,
23
+ moe_config: FusedMoEConfig,
24
quant_config: FusedMoEQuantConfig,
25
- device: torch.dtype,
25
):
26
self.is_sm100 = current_platform.has_device_capability(100)
27
super().__init__(
29
- experts=CutlassExpertsFp8(e, n, k, out_dtype, quant_config, device),
30
- fallback_experts=TritonExperts(quant_config),
28
+ experts=CutlassExpertsFp8(moe_config, quant_config),
29
+ fallback_experts=TritonExperts(moe_config, quant_config),
30
)
31
32
+ @staticmethod
33
+ def get_clses() -> tuple[
34
+ type[mk.FusedMoEPermuteExpertsUnpermute],
35
+ type[mk.FusedMoEPermuteExpertsUnpermute],
36
+ ]:
37
+ return (CutlassExpertsFp8, TritonExperts)
38
+
39
def workspace_shapes(
40
self,
41
M: int,
vllm/model_executor/layers/fused_moe/triton_deep_gemm_moe.py
+14
-4
@@ -4,7 +4,10 @@
4
import torch
5
6
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
-from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
7
+from vllm.model_executor.layers.fused_moe.config import (
8
+ FusedMoEConfig,
9
+ FusedMoEQuantConfig,
10
+)
11
from vllm.model_executor.layers.fused_moe.deep_gemm_moe import (
12
DeepGemmExperts,
13
_valid_deep_gemm,
@@ -20,12 +23,19 @@ from vllm.utils.deep_gemm import (
23
class TritonOrDeepGemmExperts(FallbackExperts):
24
"""DeepGemm with fallback to Triton for low latency shapes."""
25
23
- def __init__(self, quant_config: FusedMoEQuantConfig):
26
+ def __init__(self, moe_config: FusedMoEConfig, quant_config: FusedMoEQuantConfig):
27
super().__init__(
25
- experts=DeepGemmExperts(quant_config),
26
- fallback_experts=TritonExperts(quant_config),
28
+ experts=DeepGemmExperts(moe_config, quant_config),
29
+ fallback_experts=TritonExperts(moe_config, quant_config),
30
)
31
32
+ @staticmethod
33
+ def get_clses() -> tuple[
34
+ type[mk.FusedMoEPermuteExpertsUnpermute],
35
+ type[mk.FusedMoEPermuteExpertsUnpermute],
36
+ ]:
37
+ return (DeepGemmExperts, TritonExperts)
38
+
39
def workspace_shapes(
40
self,
41
M: int,
vllm/model_executor/layers/fused_moe/trtllm_moe.py
+47
-11
@@ -6,37 +6,73 @@ import torch
6
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
from vllm.model_executor.layers.fused_moe.config import (
8
FusedMoEConfig,
9
+ FusedMoEParallelConfig,
10
FusedMoEQuantConfig,
11
)
12
from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
13
TopKWeightAndReduceNoOP,
14
)
15
+from vllm.model_executor.layers.quantization.utils.quant_utils import (
16
+ QuantKey,
17
+)
18
19
20
class TrtLlmGenExperts(mk.FusedMoEPermuteExpertsUnpermute):
21
def __init__(
22
self,
19
- moe: FusedMoEConfig,
23
+ moe_config: FusedMoEConfig,
24
quant_config: FusedMoEQuantConfig,
25
gemm1_alpha,
26
gemm1_beta,
27
gemm1_clamp_limit,
28
max_capture_size,
29
):
26
- super().__init__(quant_config)
27
- self.moe = moe
30
+ super().__init__(moe_config, quant_config)
31
self.gemm1_alpha = gemm1_alpha
32
self.gemm1_beta = gemm1_beta
33
self.gemm1_clamp_limit = gemm1_clamp_limit
34
self.max_capture_size = max_capture_size
35
33
- @property
34
- def activation_formats(
35
- self,
36
- ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
37
- return (
38
- mk.FusedMoEActivationFormat.Standard,
39
- mk.FusedMoEActivationFormat.Standard,
36
+ @staticmethod
37
+ def activation_format() -> mk.FusedMoEActivationFormat:
38
+ return mk.FusedMoEActivationFormat.Standard
39
+
40
+ @staticmethod
41
+ def _supports_current_device() -> bool:
42
+ raise NotImplementedError(
43
+ "TrtLlmGenExperts is not yet used by an Oracle. "
44
+ "This method should not be called."
45
+ )
46
+
47
+ @staticmethod
48
+ def _supports_no_act_and_mul() -> bool:
49
+ raise NotImplementedError(
50
+ "TrtLlmGenExperts is not yet used by an Oracle. "
51
+ "This method should not be called."
52
+ )
53
+
54
+ @staticmethod
55
+ def _supports_quant_scheme(
56
+ weight_key: QuantKey | None,
57
+ activation_key: QuantKey | None,
58
+ ) -> bool:
59
+ raise NotImplementedError(
60
+ "TrtLlmGenExperts is not yet used by an Oracle. "
61
+ "This method should not be called."
62
+ )
63
+
64
+ @staticmethod
65
+ def _supports_activation(activation: str) -> bool:
66
+ raise NotImplementedError(
67
+ "TrtLlmGenExperts is not yet used by an Oracle. "
68
+ "This method should not be called."
69
+ )
70
+
71
+ @staticmethod
72
+ def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
73
+ raise NotImplementedError(
74
+ "TrtLlmGenExperts is not yet used by an Oracle. "
75
+ "This method should not be called."
76
)
77
78
def supports_chunking(self) -> bool:
@@ -86,7 +122,7 @@ class TrtLlmGenExperts(mk.FusedMoEPermuteExpertsUnpermute):
122
topk = topk_ids.size(-1)
123
local_num_experts = w1.size(0)
124
intermediate_size = w2.size(1)
89
- local_expert_offset = self.moe.ep_rank * local_num_experts
125
+ local_expert_offset = self.moe_config.ep_rank * local_num_experts
126
127
x_quant = hidden_states
128
x_scale = a1q_scale
vllm/model_executor/layers/fused_moe/unquantized_fused_moe_method.py
+6
-3
@@ -96,13 +96,17 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, CustomOp):
96
):
97
logger.debug("BatchedTritonExperts %s", self.moe)
98
return BatchedTritonExperts(
99
+ moe_config=self.moe,
100
+ quant_config=self.moe_quant_config,
101
max_num_tokens=self.moe.max_num_tokens,
102
num_dispatchers=prepare_finalize.num_dispatchers(),
101
- quant_config=self.moe_quant_config,
103
)
104
else:
105
logger.debug("TritonExperts %s", self.moe)
105
- return TritonExperts(self.moe_quant_config)
106
+ return TritonExperts(
107
+ moe_config=self.moe,
108
+ quant_config=self.moe_quant_config,
109
+ )
110
111
def create_weights(
112
self,
@@ -192,7 +196,6 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, CustomOp):
196
assert self.moe_quant_config is not None
197
198
self.kernel, self.use_inplace = make_unquantized_moe_kernel(
195
- layer=layer,
199
backend=self.unquantized_backend,
200
quant_config=self.moe_quant_config,
201
moe_config=self.moe,
vllm/model_executor/layers/quantization/awq_marlin.py
+2
@@ -739,6 +739,7 @@ class AWQMarlinMoEMethod(FusedMoEMethodBase):
739
return BatchedMarlinExperts(
740
max_num_tokens=max_num_tokens_per_rank,
741
num_dispatchers=prepare_finalize.num_dispatchers(),
742
+ moe_config=self.moe,
743
quant_config=self.moe_quant_config,
744
w13_g_idx=w13_g_idx,
745
w2_g_idx=w2_g_idx,
@@ -749,6 +750,7 @@ class AWQMarlinMoEMethod(FusedMoEMethodBase):
750
else:
751
# Standard Marlin experts for AWQ
752
return MarlinExperts(
753
+ moe_config=self.moe,
754
quant_config=self.moe_quant_config,
755
w13_g_idx=w13_g_idx,
756
w2_g_idx=w2_g_idx,
vllm/model_executor/layers/quantization/compressed_tensors/compressed_tensors_moe.py
+194
-185
@@ -19,7 +19,6 @@ from vllm.logger import init_logger
19
from vllm.model_executor.layers.fused_moe import (
20
FusedMoE,
21
FusedMoEActivationFormat,
22
- FusedMoEConfig,
22
FusedMoEMethodBase,
23
FusedMoEPermuteExpertsUnpermute,
24
FusedMoERouter,
@@ -27,9 +26,9 @@ from vllm.model_executor.layers.fused_moe import (
26
UnquantizedFusedMoEMethod,
27
)
28
from vllm.model_executor.layers.fused_moe.config import (
29
+ FusedMoEConfig,
30
FusedMoEQuantConfig,
31
- fp8_w8a8_moe_quant_config,
32
- fp8_w8a16_moe_quant_config,
31
+ RoutingMethodType,
32
int4_w4a16_moe_quant_config,
33
int4_w4afp8_moe_quant_config,
34
int8_w8a8_moe_quant_config,
@@ -45,15 +44,17 @@ from vllm.model_executor.layers.fused_moe.oracle.fp8 import (
44
Fp8MoeBackend,
45
convert_to_fp8_moe_kernel_format,
46
make_fp8_moe_kernel,
47
+ make_fp8_moe_kernel_for_mkm,
48
+ make_fp8_moe_quant_config,
49
select_fp8_moe_backend,
50
)
51
from vllm.model_executor.layers.fused_moe.oracle.nvfp4 import (
51
- FLASHINFER_NVFP4_MOE_BACKENDS,
52
NvFp4MoeBackend,
53
convert_to_nvfp4_moe_kernel_format,
54
is_global_sf_supported_for_nvfp4_backend,
55
make_mxfp4_moe_quant_config,
56
make_nvfp4_moe_kernel,
57
+ make_nvfp4_moe_kernel_for_mkm,
58
make_nvfp4_moe_quant_config,
59
select_nvfp4_moe_backend,
60
)
@@ -62,10 +63,12 @@ from vllm.model_executor.layers.quantization.compressed_tensors.schemes.compress
63
WNA16_SUPPORTED_TYPES_MAP,
64
)
65
from vllm.model_executor.layers.quantization.utils.flashinfer_fp4_moe import (
65
- build_flashinfer_fp4_cutlass_moe_prepare_finalize,
66
flashinfer_trtllm_fp4_moe,
67
flashinfer_trtllm_fp4_routed_moe,
68
- select_nvfp4_gemm_impl,
68
+)
69
+from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
70
+ apply_fi_trtllm_fp8_per_tensor_moe,
71
+ build_flashinfer_fp8_cutlass_moe_prepare_finalize,
72
)
73
from vllm.model_executor.layers.quantization.utils.fp8_utils import (
74
process_fp8_input_tensor_strategy_moe,
@@ -79,12 +82,18 @@ from vllm.model_executor.layers.quantization.utils.marlin_utils import (
82
marlin_moe_permute_scales,
83
)
84
from vllm.model_executor.layers.quantization.utils.marlin_utils_fp4 import (
82
- is_fp4_marlin_supported,
85
prepare_moe_fp4_layer_for_marlin,
86
)
87
from vllm.model_executor.layers.quantization.utils.quant_utils import (
88
convert_bf16_scales_to_fp8,
89
convert_packed_uint4b8_to_signed_int4_inplace,
90
+ kFp8Dynamic128Sym,
91
+ kFp8DynamicTokenSym,
92
+ kFp8Static128BlockSym,
93
+ kFp8StaticChannelSym,
94
+ kFp8StaticTensorSym,
95
+ kNvfp4Dynamic,
96
+ kNvfp4Static,
97
)
98
from vllm.model_executor.layers.quantization.utils.w8a8_utils import (
99
normalize_e4m3fn_to_e4m3fnuz,
@@ -200,7 +209,7 @@ class CompressedTensorsMoEMethod(FusedMoEMethodBase):
209
f"or None for NVFP4A16, found {input_quant}",
210
)
211
return CompressedTensorsW4A4Nvfp4MoEMethod(
203
- layer.moe_config, layer_name, use_marlin=input_quant is None
212
+ layer.moe_config, layer_name, use_a16=(input_quant is None)
213
)
214
elif (
215
quant_config._is_fp8_w8a8_sm90(weight_quant, input_quant)
@@ -234,6 +243,7 @@ class CompressedTensorsW4A4Mxfp4MoEMethod(CompressedTensorsMoEMethod):
243
super().__init__(moe)
244
self.group_size = 32
245
self.mxfp4_backend = NvFp4MoeBackend.MARLIN
246
+ self.experts_cls = MarlinExperts
247
self.kernel: mk.FusedMoEModularKernel | None = None
248
249
def create_weights(
@@ -327,9 +337,9 @@ class CompressedTensorsW4A4Mxfp4MoEMethod(CompressedTensorsMoEMethod):
337
self.moe_quant_config = self.get_fused_moe_quant_config(layer)
338
if self.moe_quant_config is not None:
339
self.kernel = make_nvfp4_moe_kernel(
330
- backend=self.mxfp4_backend,
331
- quant_config=self.moe_quant_config,
340
+ moe_quant_config=self.moe_quant_config,
341
moe_config=self.moe,
342
+ experts_cls=self.experts_cls,
343
)
344
345
def apply(
@@ -368,34 +378,30 @@ class CompressedTensorsW4A4Nvfp4MoEMethod(CompressedTensorsMoEMethod):
378
self,
379
moe: FusedMoEConfig,
380
layer_name: str | None = None,
371
- use_marlin: bool = False,
381
+ use_a16: bool = False,
382
):
383
super().__init__(moe)
384
self.group_size = 16
375
- if use_marlin:
376
- if is_fp4_marlin_supported():
377
- self.nvfp4_backend = NvFp4MoeBackend.MARLIN
378
- else:
379
- raise ValueError(
380
- "Marlin FP4 MoE kernel requested but not ",
381
- "supported on current platform.",
382
- )
383
- else:
384
- self.nvfp4_backend = select_nvfp4_moe_backend()
385
386
- # TODO: move this type of check into the oracle.
387
- if not self.moe.is_act_and_mul and self.nvfp4_backend not in [
388
- NvFp4MoeBackend.FLASHINFER_CUTLASS,
389
- NvFp4MoeBackend.MARLIN,
390
- ]:
391
- raise NotImplementedError(
392
- "Non-gated activations are only supported by FlashInfer "
393
- f"CUTLASS and Marlin NvFP4 MoE backends, not {self.nvfp4_backend}."
394
- )
386
+ # Select experts implementation.
387
+ self.nvfp4_backend, self.experts_cls = select_nvfp4_moe_backend(
388
+ config=self.moe,
389
+ weight_key=kNvfp4Static,
390
+ activation_key=None if use_a16 else kNvfp4Dynamic,
391
+ )
392
+
393
+ # Delay creation of the kernel until after process-weights.
394
+ self.kernel: mk.FusedMoEModularKernel | None = None
395
+
396
self.use_global_sf = is_global_sf_supported_for_nvfp4_backend(
397
self.nvfp4_backend
398
)
398
- self.kernel: mk.FusedMoEModularKernel | None = None
399
+
400
+ @property
401
+ def topk_indices_dtype(self) -> torch.dtype | None:
402
+ if self.kernel is not None:
403
+ return self.kernel.prepare_finalize.topk_indices_dtype()
404
+ return None
405
406
def create_weights(
407
self,
@@ -571,35 +577,40 @@ class CompressedTensorsW4A4Nvfp4MoEMethod(CompressedTensorsMoEMethod):
577
layer.w13_input_scale = a13_scale
578
layer.w2_input_scale = a2_scale
579
574
- # Initialize the kernel that will be called in apply().
580
+ # Setup modular kernel for TP case and naive DP/EP case.
581
+ # In non-naive DP/EP case, we will create a ModularKernelMethod.
582
+ # TODO(rob): unify these so FP8MoEMethod owns the ModularKernel
583
+ # in both cases.
584
self.moe_quant_config = self.get_fused_moe_quant_config(layer)
576
- use_dp = self.moe.dp_size > 1
577
- if self.moe_quant_config is not None and not use_dp:
585
+ if self.moe_quant_config and (
586
+ (not self.moe.moe_parallel_config.use_all2all_kernels)
587
+ or self.moe.moe_parallel_config.use_naive_all2all_kernels
588
+ ):
589
+ assert self.experts_cls is not None
590
self.kernel = make_nvfp4_moe_kernel(
579
- backend=self.nvfp4_backend,
580
- quant_config=self.moe_quant_config,
591
+ moe_quant_config=self.moe_quant_config,
592
moe_config=self.moe,
593
+ experts_cls=self.experts_cls,
594
)
595
596
def maybe_make_prepare_finalize(
597
self,
598
routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
599
) -> mk.FusedMoEPrepareAndFinalize | None:
588
- UNSUPPORTED = [NvFp4MoeBackend.MARLIN, NvFp4MoeBackend.FLASHINFER_TRTLLM]
589
- if self.nvfp4_backend in UNSUPPORTED:
600
+ if self.nvfp4_backend == NvFp4MoeBackend.FLASHINFER_TRTLLM:
601
return None
602
elif self.nvfp4_backend == NvFp4MoeBackend.FLASHINFER_CUTLASS:
592
- # TP case: avoid convert to ModularKernelMethod - to be refactored.
593
- if self.moe.dp_size == 1:
603
+ # For no-EP case, don't use the MKM framework.
604
+ if not self.moe.moe_parallel_config.use_all2all_kernels:
605
return None
595
- # For now, fp4 moe only works with the flashinfer dispatcher.
596
- prepare_finalize = build_flashinfer_fp4_cutlass_moe_prepare_finalize(
597
- self.moe
606
+
607
+ prepare_finalize = build_flashinfer_fp8_cutlass_moe_prepare_finalize(
608
+ self.moe,
609
+ use_deepseek_fp8_block_scale=False,
610
)
611
logger.debug_once("%s", prepare_finalize.__class__.__name__)
612
return prepare_finalize
601
- else:
602
- return super().maybe_make_prepare_finalize(routing_tables)
613
+ return super().maybe_make_prepare_finalize(routing_tables)
614
615
def select_gemm_impl(
616
self,
@@ -607,14 +618,13 @@ class CompressedTensorsW4A4Nvfp4MoEMethod(CompressedTensorsMoEMethod):
618
layer: torch.nn.Module,
619
) -> mk.FusedMoEPermuteExpertsUnpermute:
620
assert self.moe_quant_config is not None
610
- """Return the appropriate GEMM experts implementation."""
611
- experts = select_nvfp4_gemm_impl(
612
- self.moe,
613
- self.moe_quant_config,
614
- allow_flashinfer=(self.nvfp4_backend in FLASHINFER_NVFP4_MOE_BACKENDS),
621
+ assert self.experts_cls is not None
622
+ return make_nvfp4_moe_kernel_for_mkm(
623
+ moe_config=self.moe,
624
+ quant_config=self.moe_quant_config,
625
+ experts_cls=self.experts_cls,
626
+ prepare_finalize=prepare_finalize,
627
)
616
- logger.debug_once("Using %s", experts.__class__.__name__)
617
- return experts
628
629
def get_fused_moe_quant_config(
630
self, layer: torch.nn.Module
@@ -727,33 +737,41 @@ class CompressedTensorsW8A8Fp8MoEMethod(CompressedTensorsMoEMethod):
737
"For FP8 Fused MoE layer, we require either per tensor or "
738
"channelwise, dynamic per token quantization."
739
)
730
- self.fp8_backend = select_fp8_moe_backend(
731
- block_quant=self.block_quant,
732
- tp_size=moe.tp_size,
733
- with_lora_support=moe.is_lora_enabled,
734
- is_act_and_mul=moe.is_act_and_mul,
735
- # TODO(rob): enable selecting this externally.
740
+
741
+ ct2vllm_weight = {
742
+ QuantizationStrategy.CHANNEL: kFp8StaticChannelSym,
743
+ QuantizationStrategy.TENSOR: kFp8StaticTensorSym,
744
+ QuantizationStrategy.BLOCK: kFp8Static128BlockSym,
745
+ }
746
+ ct2vllm_act = {
747
+ QuantizationStrategy.TOKEN: kFp8DynamicTokenSym,
748
+ QuantizationStrategy.TENSOR: (
749
+ kFp8StaticTensorSym if self.static_input_scales else kFp8Dynamic128Sym
750
+ ),
751
+ }
752
+ weight_key = ct2vllm_weight[self.weight_quant.strategy]
753
+ if weight_key == kFp8Static128BlockSym:
754
+ activation_key = kFp8Dynamic128Sym
755
+ else:
756
+ activation_key = ct2vllm_act[self.input_quant.strategy]
757
+
758
+ # Select Fp8 MoE backend
759
+ self.fp8_backend, self.experts_cls = select_fp8_moe_backend(
760
+ config=self.moe,
761
+ weight_key=weight_key,
762
+ activation_key=activation_key,
763
allow_vllm_cutlass=True,
764
)
738
- if self.fp8_backend != Fp8MoeBackend.MARLIN:
739
- per_act_token = self.input_quant.strategy == QuantizationStrategy.TOKEN
740
- per_channel_quant = (
741
- self.weight_quant.strategy == QuantizationStrategy.CHANNEL
742
- )
743
- if per_act_token != per_channel_quant:
744
- raise NotImplementedError(
745
- "For FP8 Fused MoE layers, per-token and per-channel must be "
746
- "used together."
747
- )
748
- # TODO(rob): hook this up in a follow up PR.
749
- if self.fp8_backend == Fp8MoeBackend.FLASHINFER_TRTLLM:
750
- raise NotImplementedError(
751
- "FlashInfer TRTLLM backend not supported for compressed-tensors yet."
752
- )
753
- self.disable_expert_map = False
765
766
+ # Delay creation of the kernel until after process-weights.
767
self.kernel: mk.FusedMoEModularKernel | None = None
768
769
+ @property
770
+ def topk_indices_dtype(self) -> torch.dtype | None:
771
+ if self.kernel is not None:
772
+ return self.kernel.prepare_finalize.topk_indices_dtype()
773
+ return None
774
+
775
def create_weights(
776
self,
777
layer: torch.nn.Module,
@@ -970,140 +988,75 @@ class CompressedTensorsW8A8Fp8MoEMethod(CompressedTensorsMoEMethod):
988
replace_parameter(layer, "w13_weight_scale", w13_scale)
989
replace_parameter(layer, "w2_weight_scale", w2_scale)
990
991
+ # Setup modular kernel for TP case and naive DP/EP case.
992
+ # In non-naive DP/EP case, we will create a ModularKernelMethod.
993
+ # TODO(rob): unify these so FP8MoEMethod owns the ModularKernel
994
+ # in both cases.
995
self.moe_quant_config = self.get_fused_moe_quant_config(layer)
974
- if self.moe_quant_config:
996
+ if self.moe_quant_config and (
997
+ (not self.moe.moe_parallel_config.use_all2all_kernels)
998
+ or self.moe.moe_parallel_config.use_naive_all2all_kernels
999
+ ):
1000
+ assert self.experts_cls is not None
1001
self.kernel, self.use_inplace = make_fp8_moe_kernel(
976
- layer=layer,
1002
moe_quant_config=self.moe_quant_config,
1003
moe_config=self.moe,
1004
fp8_backend=self.fp8_backend,
1005
+ experts_cls=self.experts_cls,
1006
)
1007
1008
def maybe_make_prepare_finalize(
1009
self,
1010
routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
1011
) -> mk.FusedMoEPrepareAndFinalize | None:
986
- if self.fp8_backend in [Fp8MoeBackend.MARLIN, Fp8MoeBackend.AITER]:
1012
+ if self.fp8_backend == Fp8MoeBackend.FLASHINFER_TRTLLM:
1013
return None
988
- else:
989
- return super().maybe_make_prepare_finalize(routing_tables)
1014
+ elif self.fp8_backend == Fp8MoeBackend.FLASHINFER_CUTLASS:
1015
+ # For no-EP case, don't use the MKM framework.
1016
+ if not self.moe.moe_parallel_config.use_all2all_kernels:
1017
+ return None
1018
+
1019
+ prepare_finalize = build_flashinfer_fp8_cutlass_moe_prepare_finalize(
1020
+ self.moe,
1021
+ use_deepseek_fp8_block_scale=self.block_quant,
1022
+ )
1023
+ logger.debug_once("%s", prepare_finalize.__class__.__name__)
1024
+ return prepare_finalize
1025
+ return super().maybe_make_prepare_finalize(routing_tables)
1026
1027
def select_gemm_impl(
1028
self,
1029
prepare_finalize: mk.FusedMoEPrepareAndFinalize,
1030
layer: torch.nn.Module,
1031
) -> FusedMoEPermuteExpertsUnpermute:
996
- # cutlass path
1032
assert self.moe_quant_config is not None
998
- if self.fp8_backend == Fp8MoeBackend.VLLM_CUTLASS:
999
- from vllm.model_executor.layers.fused_moe import (
1000
- CutlassBatchedExpertsFp8,
1001
- CutlassExpertsFp8,
1002
- )
1003
-
1004
- experts: FusedMoEPermuteExpertsUnpermute
1005
-
1006
- num_dispatchers = prepare_finalize.num_dispatchers()
1007
-
1008
- if (
1009
- prepare_finalize.activation_format
1010
- == FusedMoEActivationFormat.BatchedExperts
1011
- ):
1012
- logger.debug("CutlassBatchedExpertsFp8(%s)", self.__class__.__name__)
1013
- experts = CutlassBatchedExpertsFp8(
1014
- max_experts_per_worker=self.moe.num_local_experts,
1015
- num_dispatchers=num_dispatchers,
1016
- out_dtype=self.moe.in_dtype,
1017
- e=layer.local_num_experts,
1018
- n=layer.intermediate_size_per_partition,
1019
- k=layer.hidden_size,
1020
- device=layer.w13_weight.device,
1021
- quant_config=self.moe_quant_config,
1022
- )
1023
- else:
1024
- logger.debug("CutlassExpertsFp8(%s)", self.__class__.__name__)
1025
- experts = CutlassExpertsFp8(
1026
- out_dtype=self.moe.in_dtype,
1027
- e=layer.local_num_experts,
1028
- n=layer.intermediate_size_per_partition,
1029
- k=layer.hidden_size,
1030
- device=layer.w13_weight.device,
1031
- quant_config=self.moe_quant_config,
1032
- )
1033
-
1034
- # TODO(rob): investigate disable_expert_map
1035
- self.disable_expert_map = (
1036
- num_dispatchers > 1 or not experts.supports_expert_map()
1037
- )
1038
-
1039
- return experts
1040
-
1041
- from vllm.model_executor.layers.fused_moe.batched_deep_gemm_moe import (
1042
- BatchedDeepGemmExperts,
1043
- )
1044
- from vllm.model_executor.layers.fused_moe.fused_batched_moe import (
1045
- BatchedTritonExperts,
1046
- )
1047
- from vllm.model_executor.layers.fused_moe.fused_moe import (
1048
- TritonExperts,
1049
- )
1050
- from vllm.model_executor.layers.fused_moe.triton_deep_gemm_moe import (
1051
- TritonOrDeepGemmExperts,
1033
+ assert self.experts_cls is not None
1034
+ return make_fp8_moe_kernel_for_mkm(
1035
+ moe_config=self.moe,
1036
+ quant_config=self.moe_quant_config,
1037
+ experts_cls=self.experts_cls,
1038
+ prepare_finalize=prepare_finalize,
1039
)
1040
1054
- assert self.fp8_backend not in [Fp8MoeBackend.AITER, Fp8MoeBackend.MARLIN]
1055
-
1056
- if (
1057
- prepare_finalize.activation_format
1058
- == FusedMoEActivationFormat.BatchedExperts
1059
- ):
1060
- max_num_tokens_per_rank = prepare_finalize.max_num_tokens_per_rank()
1061
- assert max_num_tokens_per_rank is not None
1062
-
1063
- if self.fp8_backend == Fp8MoeBackend.DEEPGEMM:
1064
- logger.debug("BatchedDeepGemmExperts(%s)", self.__class__.__name__)
1065
- return BatchedDeepGemmExperts(
1066
- max_num_tokens=max_num_tokens_per_rank,
1067
- num_dispatchers=prepare_finalize.num_dispatchers(),
1068
- quant_config=self.moe_quant_config,
1069
- )
1070
- else:
1071
- logger.debug("BatchedTritonExperts(%s)", self.__class__.__name__)
1072
- return BatchedTritonExperts(
1073
- max_num_tokens=max_num_tokens_per_rank,
1074
- num_dispatchers=prepare_finalize.num_dispatchers(),
1075
- quant_config=self.moe_quant_config,
1076
- )
1077
-
1078
- else:
1079
- if self.fp8_backend == Fp8MoeBackend.DEEPGEMM:
1080
- logger.debug("TritonOrDeepGemmExperts(%s)", self.__class__.__name__)
1081
- return TritonOrDeepGemmExperts(self.moe_quant_config)
1082
- else:
1083
- logger.debug("TritonExperts(%s)", self.__class__.__name__)
1084
- return TritonExperts(self.moe_quant_config)
1085
-
1041
def get_fused_moe_quant_config(
1042
self, layer: torch.nn.Module
1043
) -> FusedMoEQuantConfig | None:
1089
- if self.fp8_backend == Fp8MoeBackend.MARLIN:
1090
- return fp8_w8a16_moe_quant_config(
1091
- w1_scale=layer.w13_weight_scale,
1092
- w2_scale=layer.w2_weight_scale,
1093
- block_shape=self.weight_block_size,
1094
- )
1095
-
1096
- per_act_token = self.input_quant.strategy == QuantizationStrategy.TOKEN
1097
- per_channel_quant = self.weight_quant.strategy == QuantizationStrategy.CHANNEL
1044
+ w1_scale = layer.w13_weight_scale
1045
+ w2_scale = layer.w2_weight_scale
1046
+ a1_scale = layer.w13_input_scale
1047
+ a2_scale = layer.w2_input_scale
1048
1099
- return fp8_w8a8_moe_quant_config(
1100
- w1_scale=layer.w13_weight_scale,
1101
- w2_scale=layer.w2_weight_scale,
1102
- a1_scale=layer.w13_input_scale,
1103
- a2_scale=layer.w2_input_scale,
1104
- per_act_token_quant=per_act_token,
1105
- per_out_ch_quant=per_channel_quant,
1106
- block_shape=layer.weight_block_size,
1049
+ return make_fp8_moe_quant_config(
1050
+ fp8_backend=self.fp8_backend,
1051
+ w1_scale=w1_scale,
1052
+ w2_scale=w2_scale,
1053
+ a1_scale=a1_scale,
1054
+ a2_scale=a2_scale,
1055
+ per_act_token_quant=(
1056
+ self.input_quant.strategy == QuantizationStrategy.TOKEN
1057
+ ),
1058
+ per_out_ch_quant=(self.input_quant.strategy == QuantizationStrategy.TOKEN),
1059
+ block_shape=self.weight_block_size,
1060
)
1061
1062
def apply(
@@ -1113,6 +1066,56 @@ class CompressedTensorsW8A8Fp8MoEMethod(CompressedTensorsMoEMethod):
1066
x: torch.Tensor,
1067
router_logits: torch.Tensor,
1068
) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
1069
+ if self.fp8_backend == Fp8MoeBackend.FLASHINFER_TRTLLM:
1070
+ if layer.enable_eplb:
1071
+ raise NotImplementedError(
1072
+ "EPLB not supported for `FlashInfer TRTLLM FP8 MoE`."
1073
+ )
1074
+ assert layer.activation == "silu"
1075
+
1076
+ if self.block_quant:
1077
+ import vllm.model_executor.layers.fused_moe.flashinfer_trtllm_moe # noqa: E501, F401
1078
+
1079
+ e_score_correction_bias = (
1080
+ layer.e_score_correction_bias.to(x.dtype)
1081
+ if layer.e_score_correction_bias is not None
1082
+ else None
1083
+ )
1084
+ routing_method_type = layer.routing_method_type
1085
+ return torch.ops.vllm.flashinfer_fused_moe_blockscale_fp8(
1086
+ routing_logits=router_logits.to(torch.float32)
1087
+ if routing_method_type == RoutingMethodType.DeepSeekV3
1088
+ else router_logits,
1089
+ routing_bias=e_score_correction_bias,
1090
+ x=x,
1091
+ w13_weight=layer.w13_weight,
1092
+ w13_weight_scale_inv=layer.w13_weight_scale,
1093
+ w2_weight=layer.w2_weight,
1094
+ w2_weight_scale_inv=layer.w2_weight_scale,
1095
+ global_num_experts=layer.global_num_experts,
1096
+ top_k=layer.top_k,
1097
+ num_expert_group=layer.num_expert_group,
1098
+ topk_group=layer.topk_group,
1099
+ intermediate_size=layer.intermediate_size_per_partition,
1100
+ expert_offset=layer.ep_rank * layer.local_num_experts,
1101
+ local_num_experts=layer.local_num_experts,
1102
+ block_shape=self.weight_block_size,
1103
+ routing_method_type=routing_method_type,
1104
+ routed_scaling=layer.routed_scaling_factor,
1105
+ )
1106
+ else:
1107
+ return apply_fi_trtllm_fp8_per_tensor_moe(
1108
+ layer=layer,
1109
+ hidden_states=x,
1110
+ router_logits=router_logits,
1111
+ routing_bias=layer.e_score_correction_bias,
1112
+ global_num_experts=layer.global_num_experts,
1113
+ top_k=layer.top_k,
1114
+ num_expert_group=layer.num_expert_group,
1115
+ topk_group=layer.topk_group,
1116
+ apply_router_weight_on_input=layer.apply_router_weight_on_input,
1117
+ )
1118
+
1119
topk_weights, topk_ids = router.select_experts(
1120
hidden_states=x,
1121
router_logits=router_logits,
@@ -1130,7 +1133,7 @@ class CompressedTensorsW8A8Fp8MoEMethod(CompressedTensorsMoEMethod):
1133
global_num_experts=layer.global_num_experts,
1134
# TODO(rob): investigate the disable_expert_map introduced by:
1135
# https://github.com/vllm-project/vllm/commit/84166fee9770e6fba71a96978b3e7d149392fb28 # noqa: E501
1133
- expert_map=None if self.disable_expert_map else layer.expert_map,
1136
+ expert_map=layer.expert_map,
1137
apply_router_weight_on_input=layer.apply_router_weight_on_input,
1138
)
1139
@@ -1596,6 +1599,7 @@ class CompressedTensorsWNA16MarlinMoEMethod(CompressedTensorsMoEMethod):
1599
return BatchedMarlinExperts(
1600
max_num_tokens=max_num_tokens_per_rank,
1601
num_dispatchers=prepare_finalize.num_dispatchers(),
1602
+ moe_config=self.moe,
1603
quant_config=self.moe_quant_config,
1604
w13_g_idx=layer.w13_weight_g_idx,
1605
w2_g_idx=layer.w2_weight_g_idx,
@@ -1605,6 +1609,7 @@ class CompressedTensorsWNA16MarlinMoEMethod(CompressedTensorsMoEMethod):
1609
)
1610
else:
1611
return MarlinExperts(
1612
+ moe_config=self.moe,
1613
quant_config=self.moe_quant_config,
1614
w13_g_idx=layer.w13_weight_g_idx,
1615
w2_g_idx=layer.w2_weight_g_idx,
@@ -1854,7 +1859,9 @@ class CompressedTensorsWNA16MoEMethod(CompressedTensorsMoEMethod):
1859
1860
layer.w13_weight = layer.w13_weight_packed
1861
layer.w2_weight = layer.w2_weight_packed
1857
- return TritonWNA16Experts(quant_config=self.moe_quant_config)
1862
+ return TritonWNA16Experts(
1863
+ moe_config=self.moe, quant_config=self.moe_quant_config
1864
+ )
1865
else:
1866
raise NotImplementedError(
1867
"TritonExperts requires Triton. "
@@ -2467,6 +2474,7 @@ class CompressedTensorsW4A8Fp8MoEMethod(CompressedTensorsMoEMethod):
2474
c_strides2=self.a_strides1_c_strides2,
2475
s_strides1=self.s_strides1,
2476
s_strides2=self.s_strides2,
2477
+ moe_config=self.moe,
2478
quant_config=self.moe_quant_config,
2479
group_size=self.group_size,
2480
)
@@ -2505,6 +2513,7 @@ class CompressedTensorsW4A8Fp8MoEMethod(CompressedTensorsMoEMethod):
2513
layer.w2_weight_packed,
2514
topk_weights,
2515
topk_ids,
2516
+ moe_config=self.moe,
2517
quant_config=self.moe_quant_config,
2518
activation=layer.activation,
2519
global_num_experts=layer.global_num_experts,
vllm/model_executor/layers/quantization/fp8.py
+51
-104
@@ -19,7 +19,6 @@ from vllm.model_executor.layers.batch_invariant import (
19
)
20
from vllm.model_executor.layers.fused_moe import (
21
FusedMoE,
22
- FusedMoEActivationFormat,
22
FusedMoEMethodBase,
23
FusedMoEPermuteExpertsUnpermute,
24
FusedMoEPrepareAndFinalize,
@@ -35,6 +34,7 @@ from vllm.model_executor.layers.fused_moe.oracle.fp8 import (
34
Fp8MoeBackend,
35
convert_to_fp8_moe_kernel_format,
36
make_fp8_moe_kernel,
37
+ make_fp8_moe_kernel_for_mkm,
38
make_fp8_moe_quant_config,
39
select_fp8_moe_backend,
40
)
@@ -55,7 +55,6 @@ from vllm.model_executor.layers.quantization.kv_cache import BaseKVCacheMethod
55
from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
56
apply_fi_trtllm_fp8_per_tensor_moe,
57
build_flashinfer_fp8_cutlass_moe_prepare_finalize,
58
- select_cutlass_fp8_gemm_impl,
58
)
59
from vllm.model_executor.layers.quantization.utils.fp8_utils import (
60
W8A8BlockFp8LinearOp,
@@ -79,8 +78,10 @@ from vllm.model_executor.layers.quantization.utils.marlin_utils_fp8 import (
78
from vllm.model_executor.layers.quantization.utils.quant_utils import (
79
GroupShape,
80
is_layer_skipped,
81
+ kFp8Dynamic128Sym,
82
kFp8DynamicTensorSym,
83
kFp8DynamicTokenSym,
84
+ kFp8Static128BlockSym,
85
kFp8StaticTensorSym,
86
)
87
from vllm.model_executor.layers.quantization.utils.w8a8_utils import (
@@ -658,38 +659,36 @@ class Fp8MoEMethod(FusedMoEMethodBase):
659
self.weight_scale_name = (
660
"weight_scale_inv" if self.block_quant else "weight_scale"
661
)
661
- self.fp8_backend = select_fp8_moe_backend(
662
- block_quant=self.block_quant,
663
- tp_size=layer.moe_parallel_config.tp_size,
664
- with_lora_support=self.moe.is_lora_enabled,
665
- is_act_and_mul=self.moe.is_act_and_mul,
666
- )
662
668
- if self.fp8_backend == Fp8MoeBackend.FLASHINFER_CUTLASS:
669
- if self.block_quant and self.weight_block_size != [128, 128]:
670
- raise NotImplementedError(
671
- "FlashInfer CUTLASS FP8 MoE backend only supports block "
672
- "size [128, 128]."
673
- )
674
- if layer.activation != "silu":
675
- raise NotImplementedError(
676
- "FlashInfer CUTLASS FP8 MoE backend only supports SiLU "
677
- "activation function, but got {layer.activation}."
678
- )
679
- dynamic_per_token = (
680
- not self.block_quant and self.quant_config.activation_scheme != "static"
681
- )
682
- if dynamic_per_token and self.fp8_backend in [
683
- Fp8MoeBackend.FLASHINFER_TRTLLM,
684
- Fp8MoeBackend.FLASHINFER_CUTLASS,
685
- ]:
686
- raise NotImplementedError(
687
- "FlashInfer FP8 MoE backend does not support dynamic per token "
688
- "activation quantization."
663
+ # Set weight key and activation key for kernel compatibility
664
+ if self.block_quant:
665
+ weight_key = kFp8Static128BlockSym
666
+ activation_key = kFp8Dynamic128Sym
667
+ else:
668
+ weight_key = kFp8StaticTensorSym
669
+ activation_key = (
670
+ kFp8StaticTensorSym
671
+ if self.quant_config.activation_scheme == "static"
672
+ else kFp8DynamicTensorSym
673
)
674
675
+ # Select Fp8 MoE backend
676
+ self.fp8_backend, self.experts_cls = select_fp8_moe_backend(
677
+ config=self.moe,
678
+ weight_key=weight_key,
679
+ activation_key=activation_key,
680
+ allow_vllm_cutlass=False,
681
+ )
682
+
683
+ # Delay creation of the kernel until after process-weights.
684
self.kernel: mk.FusedMoEModularKernel | None = None
685
686
+ @property
687
+ def topk_indices_dtype(self) -> torch.dtype | None:
688
+ if self.kernel is not None:
689
+ return self.kernel.prepare_finalize.topk_indices_dtype()
690
+ return None
691
+
692
def create_weights(
693
self,
694
layer: Module,
@@ -842,14 +841,21 @@ class Fp8MoEMethod(FusedMoEMethodBase):
841
replace_parameter(layer, f"w13_{self.weight_scale_name}", w13_scale)
842
replace_parameter(layer, f"w2_{self.weight_scale_name}", w2_scale)
843
845
- # Setup modular kernel for TP case.
844
+ # Setup modular kernel for TP case and naive DP/EP case.
845
+ # In non-naive DP/EP case, we will create a ModularKernelMethod.
846
+ # TODO(rob): unify these so FP8MoEMethod owns the ModularKernel
847
+ # in both cases.
848
self.moe_quant_config = self.get_fused_moe_quant_config(layer)
847
- if self.moe_quant_config:
849
+ if self.moe_quant_config and (
850
+ (not self.moe.moe_parallel_config.use_all2all_kernels)
851
+ or self.moe.moe_parallel_config.use_naive_all2all_kernels
852
+ ):
853
+ assert self.experts_cls is not None
854
self.kernel, self.use_inplace = make_fp8_moe_kernel(
849
- layer=layer,
855
moe_quant_config=self.moe_quant_config,
856
moe_config=self.moe,
857
fp8_backend=self.fp8_backend,
858
+ experts_cls=self.experts_cls,
859
)
860
861
def process_weights_after_loading(self, layer: Module) -> None:
@@ -904,13 +910,13 @@ class Fp8MoEMethod(FusedMoEMethodBase):
910
self,
911
routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
912
) -> mk.FusedMoEPrepareAndFinalize | None:
907
- if self.fp8_backend in [
908
- Fp8MoeBackend.AITER,
909
- Fp8MoeBackend.MARLIN,
910
- Fp8MoeBackend.FLASHINFER_TRTLLM,
911
- ]:
913
+ if self.fp8_backend == Fp8MoeBackend.FLASHINFER_TRTLLM:
914
return None
915
elif self.fp8_backend == Fp8MoeBackend.FLASHINFER_CUTLASS:
916
+ # For no-EP case, don't use the MKM framework.
917
+ if not self.moe.moe_parallel_config.use_all2all_kernels:
918
+ return None
919
+
920
prepare_finalize = build_flashinfer_fp8_cutlass_moe_prepare_finalize(
921
self.moe,
922
use_deepseek_fp8_block_scale=self.block_quant,
@@ -924,73 +930,14 @@ class Fp8MoEMethod(FusedMoEMethodBase):
930
prepare_finalize: FusedMoEPrepareAndFinalize,
931
layer: torch.nn.Module,
932
) -> FusedMoEPermuteExpertsUnpermute:
927
- from vllm.model_executor.layers.fused_moe import (
928
- BatchedDeepGemmExperts,
929
- BatchedTritonExperts,
930
- TritonExperts,
931
- TritonOrDeepGemmExperts,
932
- )
933
-
934
- if self.fp8_backend in [Fp8MoeBackend.MARLIN, Fp8MoeBackend.AITER]:
935
- raise NotImplementedError(
936
- "Marlin and ROCm AITER are not supported with all2all yet."
937
- )
938
-
933
assert self.moe_quant_config is not None
940
-
941
- if (
942
- prepare_finalize.activation_format
943
- == FusedMoEActivationFormat.BatchedExperts
944
- ):
945
- max_num_tokens_per_rank = prepare_finalize.max_num_tokens_per_rank()
946
- assert max_num_tokens_per_rank is not None
947
-
948
- experts_impl = (
949
- BatchedDeepGemmExperts
950
- if self.fp8_backend == Fp8MoeBackend.DEEPGEMM
951
- else BatchedTritonExperts
952
- )
953
- logger.debug(
954
- "%s(%s): max_tokens_per_rank=%s, block_size=%s, per_act_token=%s",
955
- experts_impl.__name__,
956
- self.__class__.__name__,
957
- max_num_tokens_per_rank,
958
- self.weight_block_size,
959
- False,
960
- )
961
- return experts_impl(
962
- max_num_tokens=max_num_tokens_per_rank,
963
- num_dispatchers=prepare_finalize.num_dispatchers(),
964
- quant_config=self.moe_quant_config,
965
- )
966
- elif self.moe.is_lora_enabled:
967
- return TritonExperts(quant_config=self.moe_quant_config)
968
- elif self.fp8_backend == Fp8MoeBackend.FLASHINFER_CUTLASS:
969
- # Select GEMM experts with block-scale when weights are block-quantized
970
- experts = select_cutlass_fp8_gemm_impl(
971
- self.moe,
972
- self.moe_quant_config,
973
- use_deepseek_fp8_block_scale=self.block_quant,
974
- )
975
- logger.debug_once("Using %s", experts.__class__.__name__)
976
- return experts
977
- elif self.fp8_backend == Fp8MoeBackend.DEEPGEMM:
978
- logger.debug(
979
- "TritonOrDeepGemmExperts(%s): block_size=%s, per_act_token=%s",
980
- self.__class__.__name__,
981
- self.weight_block_size,
982
- False,
983
- )
984
- return TritonOrDeepGemmExperts(self.moe_quant_config)
985
- else:
986
- assert self.fp8_backend == Fp8MoeBackend.TRITON
987
- logger.debug(
988
- "TritonExperts(%s): block_size=%s, per_act_token=%s",
989
- self.__class__.__name__,
990
- self.weight_block_size,
991
- False,
992
- )
993
- return TritonExperts(self.moe_quant_config)
934
+ assert self.experts_cls is not None
935
+ return make_fp8_moe_kernel_for_mkm(
936
+ moe_config=self.moe,
937
+ quant_config=self.moe_quant_config,
938
+ experts_cls=self.experts_cls,
939
+ prepare_finalize=prepare_finalize,
940
+ )
941
942
def get_fused_moe_quant_config(
943
self, layer: torch.nn.Module
@@ -1067,7 +1014,7 @@ class Fp8MoEMethod(FusedMoEMethodBase):
1014
routed_scaling=layer.routed_scaling_factor,
1015
)
1016
else:
1070
- result = apply_fi_trtllm_fp8_per_tensor_moe(
1017
+ return apply_fi_trtllm_fp8_per_tensor_moe(
1018
layer=layer,
1019
hidden_states=x,
1020
router_logits=router_logits,
vllm/model_executor/layers/quantization/gptq_marlin.py
+2
@@ -875,6 +875,7 @@ class GPTQMarlinMoEMethod(FusedMoEMethodBase):
875
return BatchedMarlinExperts(
876
max_num_tokens=max_num_tokens_per_rank,
877
num_dispatchers=prepare_finalize.num_dispatchers(),
878
+ moe_config=self.moe,
879
quant_config=self.moe_quant_config,
880
w13_g_idx=w13_g_idx,
881
w2_g_idx=w2_g_idx,
@@ -885,6 +886,7 @@ class GPTQMarlinMoEMethod(FusedMoEMethodBase):
886
else:
887
# Standard Marlin experts for GPTQ
888
return MarlinExperts(
889
+ moe_config=self.moe,
890
quant_config=self.moe_quant_config,
891
w13_g_idx=w13_g_idx,
892
w2_g_idx=w2_g_idx,
vllm/model_executor/layers/quantization/modelopt.py
+64
-42
@@ -27,15 +27,16 @@ from vllm.model_executor.layers.fused_moe.oracle.fp8 import (
27
Fp8MoeBackend,
28
convert_to_fp8_moe_kernel_format,
29
make_fp8_moe_kernel,
30
+ make_fp8_moe_kernel_for_mkm,
31
make_fp8_moe_quant_config,
32
select_fp8_moe_backend,
33
)
34
from vllm.model_executor.layers.fused_moe.oracle.nvfp4 import (
34
- FLASHINFER_NVFP4_MOE_BACKENDS,
35
NvFp4MoeBackend,
36
convert_to_nvfp4_moe_kernel_format,
37
is_global_sf_supported_for_nvfp4_backend,
38
make_nvfp4_moe_kernel,
39
+ make_nvfp4_moe_kernel_for_mkm,
40
make_nvfp4_moe_quant_config,
41
select_nvfp4_moe_backend,
42
)
@@ -57,12 +58,10 @@ from vllm.model_executor.layers.quantization.utils.flashinfer_fp4_moe import (
58
build_flashinfer_fp4_cutlass_moe_prepare_finalize,
59
flashinfer_trtllm_fp4_moe,
60
flashinfer_trtllm_fp4_routed_moe,
60
- select_nvfp4_gemm_impl,
61
)
62
from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
63
apply_fi_trtllm_fp8_per_tensor_moe,
64
build_flashinfer_fp8_cutlass_moe_prepare_finalize,
65
- select_cutlass_fp8_gemm_impl,
65
)
66
from vllm.model_executor.layers.quantization.utils.fp8_utils import (
67
W8A8BlockFp8LinearOp,
@@ -84,6 +83,8 @@ from vllm.model_executor.layers.quantization.utils.quant_utils import (
83
kFp8DynamicTokenSym,
84
kFp8StaticTensorSym,
85
kFp8StaticTokenSym,
86
+ kNvfp4Dynamic,
87
+ kNvfp4Static,
88
swizzle_blockscale,
89
)
90
from vllm.model_executor.layers.quantization.utils.w8a8_utils import (
@@ -728,14 +729,23 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
729
super().__init__(moe_config)
730
self.quant_config = quant_config
731
assert self.quant_config.is_checkpoint_fp8_serialized
731
- self.fp8_backend = select_fp8_moe_backend(
732
- block_quant=False,
733
- tp_size=moe_config.moe_parallel_config.tp_size,
734
- with_lora_support=self.moe.is_lora_enabled,
735
- is_act_and_mul=self.moe.is_act_and_mul,
732
+
733
+ # Select Fp8 MoE backend
734
+ self.fp8_backend, self.experts_cls = select_fp8_moe_backend(
735
+ config=self.moe,
736
+ weight_key=kFp8StaticTensorSym,
737
+ activation_key=kFp8StaticTensorSym,
738
)
739
+
740
+ # Delay creation of the kernel until after process-weights.
741
self.kernel: mk.FusedMoEModularKernel | None = None
742
743
+ @property
744
+ def topk_indices_dtype(self) -> torch.dtype | None:
745
+ if self.kernel is not None:
746
+ return self.kernel.prepare_finalize.topk_indices_dtype()
747
+ return None
748
+
749
def maybe_make_prepare_finalize(
750
self,
751
routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
@@ -744,8 +754,8 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
754
if self.fp8_backend == Fp8MoeBackend.FLASHINFER_TRTLLM:
755
return None
756
elif self.fp8_backend == Fp8MoeBackend.FLASHINFER_CUTLASS:
747
- # TP case: avoid convert to ModularKernelMethod - to be refactored.
748
- if self.moe.dp_size == 1:
757
+ # For no-EP case, don't use the MKM framework.
758
+ if not self.moe.moe_parallel_config.use_all2all_kernels:
759
return None
760
761
prepare_finalize = build_flashinfer_fp8_cutlass_moe_prepare_finalize(
@@ -762,12 +772,13 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
772
layer: torch.nn.Module,
773
) -> mk.FusedMoEPermuteExpertsUnpermute:
774
assert self.moe_quant_config is not None
765
- experts = select_cutlass_fp8_gemm_impl(
766
- self.moe,
767
- self.moe_quant_config,
775
+ assert self.experts_cls is not None
776
+ return make_fp8_moe_kernel_for_mkm(
777
+ moe_config=self.moe,
778
+ quant_config=self.moe_quant_config,
779
+ experts_cls=self.experts_cls,
780
+ prepare_finalize=prepare_finalize,
781
)
769
- logger.debug_once("Using %s", experts.__class__.__name__)
770
- return experts
782
783
def create_weights(
784
self,
@@ -876,14 +887,15 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
887
replace_parameter(layer, "w13_weight_scale", w13_scale)
888
replace_parameter(layer, "w2_weight_scale", w2_scale)
889
879
- # Setup modular kernel for TP case.
890
+ # Setup modular kernel.
891
self.moe_quant_config = self.get_fused_moe_quant_config(layer)
892
if self.moe_quant_config:
893
+ assert self.experts_cls is not None
894
self.kernel, self.use_inplace = make_fp8_moe_kernel(
883
- layer=layer,
895
moe_quant_config=self.moe_quant_config,
896
moe_config=self.moe,
897
fp8_backend=self.fp8_backend,
898
+ experts_cls=self.experts_cls,
899
)
900
901
def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
@@ -1335,32 +1347,35 @@ class ModelOptNvFp4FusedMoE(FusedMoEMethodBase):
1347
) -> None:
1348
super().__init__(moe_config)
1349
self.quant_config = quant_config
1338
- self.nvfp4_backend = select_nvfp4_moe_backend()
1339
- # TODO: move this type of check into the oracle.
1340
- if not self.moe.is_act_and_mul and self.nvfp4_backend not in [
1341
- NvFp4MoeBackend.FLASHINFER_CUTLASS,
1342
- NvFp4MoeBackend.MARLIN,
1343
- ]:
1344
- raise NotImplementedError(
1345
- "Non-gated activations are only supported by FlashInfer "
1346
- f"CUTLASS and Marlin NvFP4 MoE backends, not {self.nvfp4_backend}."
1347
- )
1350
+ # Select experts implementation.
1351
+ self.nvfp4_backend, self.experts_cls = select_nvfp4_moe_backend(
1352
+ config=self.moe,
1353
+ weight_key=kNvfp4Static,
1354
+ activation_key=kNvfp4Dynamic,
1355
+ )
1356
+
1357
+ # Delay creation of the kernel until after process-weights.
1358
+ self.kernel: mk.FusedMoEModularKernel | None = None
1359
1360
self.use_global_sf = is_global_sf_supported_for_nvfp4_backend(
1361
self.nvfp4_backend
1362
)
1352
- self.kernel: mk.FusedMoEModularKernel | None = None
1363
+
1364
+ @property
1365
+ def topk_indices_dtype(self) -> torch.dtype | None:
1366
+ if self.kernel is not None:
1367
+ return self.kernel.prepare_finalize.topk_indices_dtype()
1368
+ return None
1369
1370
def maybe_make_prepare_finalize(
1371
self,
1372
routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
1373
) -> mk.FusedMoEPrepareAndFinalize | None:
1358
- UNSUPPORTED = [NvFp4MoeBackend.MARLIN, NvFp4MoeBackend.FLASHINFER_TRTLLM]
1359
- if self.nvfp4_backend in UNSUPPORTED:
1374
+ if self.nvfp4_backend == NvFp4MoeBackend.FLASHINFER_TRTLLM:
1375
return None
1376
elif self.nvfp4_backend == NvFp4MoeBackend.FLASHINFER_CUTLASS:
1362
- # TP case: avoid convert to ModularKernelMethod - to be refactored.
1363
- if self.moe.dp_size == 1:
1377
+ # For no-EP case, don't use the MKM framework.
1378
+ if not self.moe.moe_parallel_config.use_all2all_kernels:
1379
return None
1380
# For now, fp4 moe only works with the flashinfer dispatcher.
1381
prepare_finalize = build_flashinfer_fp4_cutlass_moe_prepare_finalize(
@@ -1377,13 +1392,13 @@ class ModelOptNvFp4FusedMoE(FusedMoEMethodBase):
1392
layer: torch.nn.Module,
1393
) -> mk.FusedMoEPermuteExpertsUnpermute:
1394
assert self.moe_quant_config is not None
1380
- experts = select_nvfp4_gemm_impl(
1381
- self.moe,
1382
- self.moe_quant_config,
1383
- allow_flashinfer=self.nvfp4_backend in FLASHINFER_NVFP4_MOE_BACKENDS,
1395
+ assert self.experts_cls is not None
1396
+ return make_nvfp4_moe_kernel_for_mkm(
1397
+ moe_config=self.moe,
1398
+ quant_config=self.moe_quant_config,
1399
+ experts_cls=self.experts_cls,
1400
+ prepare_finalize=prepare_finalize,
1401
)
1385
- logger.debug_once("Using %s", experts.__class__.__name__)
1386
- return experts
1402
1403
def uses_weight_scale_2_pattern(self) -> bool:
1404
"""
@@ -1554,13 +1569,20 @@ class ModelOptNvFp4FusedMoE(FusedMoEMethodBase):
1569
replace_parameter(layer, "w2_weight_scale_2", w2_scale_2)
1570
replace_parameter(layer, "w2_input_scale", a2_scale)
1571
1572
+ # Setup modular kernel for TP case and naive DP/EP case.
1573
+ # In non-naive DP/EP case, we will create a ModularKernelMethod.
1574
+ # TODO(rob): unify these so FP8MoEMethod owns the ModularKernel
1575
+ # in both cases.
1576
self.moe_quant_config = self.get_fused_moe_quant_config(layer)
1558
- use_dp = self.moe.dp_size > 1
1559
- if self.moe_quant_config is not None and not use_dp:
1577
+ if self.moe_quant_config and (
1578
+ (not self.moe.moe_parallel_config.use_all2all_kernels)
1579
+ or self.moe.moe_parallel_config.use_naive_all2all_kernels
1580
+ ):
1581
+ assert self.experts_cls is not None
1582
self.kernel = make_nvfp4_moe_kernel(
1561
- backend=self.nvfp4_backend,
1562
- quant_config=self.moe_quant_config,
1583
+ moe_quant_config=self.moe_quant_config,
1584
moe_config=self.moe,
1585
+ experts_cls=self.experts_cls,
1586
)
1587
1588
@property
vllm/model_executor/layers/quantization/mxfp4.py
+4
-3
@@ -853,6 +853,7 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
853
max_num_tokens=max_num_tokens_per_rank,
854
num_dispatchers=prepare_finalize.num_dispatchers(),
855
quant_config=self.moe_quant_config,
856
+ moe_config=self.moe,
857
)
858
else:
859
raise NotImplementedError(
@@ -875,11 +876,11 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
876
}
877
return TrtLlmGenExperts(self.moe, self.moe_quant_config, **kwargs)
878
elif self.mxfp4_backend == Mxfp4Backend.MARLIN:
878
- return MarlinExperts(self.moe_quant_config)
879
+ return MarlinExperts(self.moe, self.moe_quant_config)
880
elif self.mxfp4_backend == Mxfp4Backend.TRITON:
881
if self.moe.is_lora_enabled:
881
- return UnfusedOAITritonExperts(self.moe_quant_config)
882
- return OAITritonExperts(self.moe_quant_config)
882
+ return UnfusedOAITritonExperts(self.moe, self.moe_quant_config)
883
+ return OAITritonExperts(self.moe, self.moe_quant_config)
884
else:
885
raise NotImplementedError(
886
f"Incompatible Mxfp4 backend ({self.mxfp4_backend}) for EP"
vllm/model_executor/layers/quantization/utils/flashinfer_fp4_moe.py
+84
-38
@@ -11,19 +11,16 @@ import vllm.model_executor.layers.fused_moe.modular_kernel as mk
11
from vllm.logger import init_logger
12
from vllm.model_executor.layers.fused_moe.config import (
13
FusedMoEConfig,
14
- FusedMoEQuantConfig,
14
+ FusedMoEParallelConfig,
15
RoutingMethodType,
16
)
17
-from vllm.model_executor.layers.fused_moe.flashinfer_cutedsl_moe import (
18
- FlashInferCuteDSLExperts,
19
-)
20
-from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_moe import (
21
- FlashInferExperts,
22
-)
17
from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_prepare_finalize import ( # noqa: E501
18
create_flashinfer_prepare_finalize,
19
)
20
from vllm.model_executor.layers.quantization.utils.quant_utils import (
21
+ QuantKey,
22
+ kNvfp4Dynamic,
23
+ kNvfp4Static,
24
swizzle_blockscale,
25
)
26
from vllm.platforms import current_platform
@@ -47,6 +44,86 @@ __all__ = [
44
"build_flashinfer_fp4_cutlass_moe_prepare_finalize",
45
]
46
47
+#
48
+# Methods used by the oracle for kernel selection.
49
+#
50
+
51
+
52
+def _supports_current_device() -> bool:
53
+ """Supports only Blackwell-family GPUs."""
54
+ p = current_platform
55
+ return p.is_cuda() and p.is_device_capability_family(100)
56
+
57
+
58
+def _supports_no_act_and_mul() -> bool:
59
+ """Does not support non-gated MoE (i.e. Nemotron-Nano)."""
60
+ return False
61
+
62
+
63
+def _supports_quant_scheme(
64
+ weight_key: QuantKey | None,
65
+ activation_key: QuantKey | None,
66
+) -> bool:
67
+ """Supports Nvfp4 quantization."""
68
+ SUPPORTED_W_A = [
69
+ (kNvfp4Static, kNvfp4Dynamic),
70
+ ]
71
+ return (weight_key, activation_key) in SUPPORTED_W_A
72
+
73
+
74
+def _supports_activation(activation: str) -> bool:
75
+ """Supports silu activation only."""
76
+ return activation in ["silu"]
77
+
78
+
79
+def _supports_routing_method(
80
+ routing_method: RoutingMethodType,
81
+) -> bool:
82
+ """Monolithic kernels need to express router support."""
83
+ # NOTE(rob): potentially allow others here. This is a conservative list.
84
+ return routing_method in [
85
+ RoutingMethodType.DeepSeekV3,
86
+ RoutingMethodType.Renormalize,
87
+ RoutingMethodType.RenormalizeNaive,
88
+ RoutingMethodType.Llama4,
89
+ ]
90
+
91
+
92
+def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
93
+ """Supports EP."""
94
+ return True
95
+
96
+
97
+def is_supported_config_trtllm(
98
+ moe_config: FusedMoEConfig,
99
+ weight_key: QuantKey | None,
100
+ activation_key: QuantKey | None,
101
+ activation_format: mk.FusedMoEActivationFormat,
102
+) -> tuple[bool, str | None]:
103
+ """
104
+ This method mirrors mk.FusedMoEPermuteExpertsUnpermute.is_supported_config
105
+ """
106
+
107
+ def _make_reason(reason: str) -> str:
108
+ return f"kernel does not support {reason}"
109
+
110
+ if not _supports_current_device():
111
+ return False, _make_reason("current device")
112
+ elif not (moe_config.is_act_and_mul or _supports_no_act_and_mul()):
113
+ return False, _make_reason("no act_and_mul MLP layer")
114
+ elif not _supports_activation(moe_config.activation):
115
+ return False, _make_reason(f"{moe_config.activation} activation")
116
+ elif not _supports_quant_scheme(weight_key, activation_key):
117
+ return False, _make_reason("quantization scheme")
118
+ elif not _supports_parallel_config(moe_config.moe_parallel_config):
119
+ return False, _make_reason("parallel config")
120
+ elif not _supports_routing_method(moe_config.routing_method):
121
+ return False, _make_reason("routing method")
122
+ elif activation_format != mk.FusedMoEActivationFormat.Standard:
123
+ return False, _make_reason("activation format")
124
+
125
+ return True, None
126
+
127
128
def is_flashinfer_fp4_cutlass_moe_available() -> bool:
129
"""Return `True` when FlashInfer CUTLASS NV-FP4 kernels can be used."""
@@ -96,37 +173,6 @@ def build_flashinfer_fp4_cutlass_moe_prepare_finalize(
173
)
174
175
99
-def select_nvfp4_gemm_impl(
100
- moe: FusedMoEConfig,
101
- moe_quant_config: FusedMoEQuantConfig,
102
- allow_flashinfer: bool,
103
-) -> mk.FusedMoEPermuteExpertsUnpermute:
104
- """Return a GEMM *experts* implementation for NV-FP4 fused-MoE layers"""
105
-
106
- if allow_flashinfer:
107
- if envs.VLLM_FLASHINFER_MOE_BACKEND == "masked_gemm":
108
- return FlashInferCuteDSLExperts(
109
- out_dtype=moe.in_dtype,
110
- quant_config=moe_quant_config,
111
- )
112
- elif envs.VLLM_FLASHINFER_MOE_BACKEND == "throughput":
113
- return FlashInferExperts(
114
- out_dtype=moe.in_dtype,
115
- quant_config=moe_quant_config,
116
- ep_rank=moe.moe_parallel_config.ep_rank,
117
- ep_size=moe.moe_parallel_config.ep_size,
118
- tp_rank=moe.moe_parallel_config.tp_rank,
119
- tp_size=moe.moe_parallel_config.tp_size,
120
- use_dp=moe.moe_parallel_config.dp_size > 1,
121
- )
122
-
123
- # native cutlass experts currently don't support DP; TP case won't call this
124
- raise ValueError(
125
- "CutlassExpertsFp4 doesn't support DP. Use flashinfer CUTLASS "
126
- "Fused MoE backend instead (set VLLM_USE_FLASHINFER_MOE_FP4=1)"
127
- )
128
-
129
-
176
def prepare_static_weights_for_trtllm_fp4_moe(
177
# args_dequant,
178
# args,
vllm/model_executor/layers/quantization/utils/flashinfer_utils.py
-31
@@ -9,10 +9,6 @@ from vllm import envs
9
from vllm.logger import init_logger
10
from vllm.model_executor.layers.fused_moe.config import (
11
FusedMoEConfig,
12
- FusedMoEQuantConfig,
13
-)
14
-from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_moe import (
15
- FlashInferExperts,
12
)
13
from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_prepare_finalize import ( # noqa: E501
14
create_flashinfer_prepare_finalize,
@@ -203,33 +199,6 @@ def build_flashinfer_fp8_cutlass_moe_prepare_finalize(
199
)
200
201
206
-def select_cutlass_fp8_gemm_impl(
207
- moe: FusedMoEConfig | None,
208
- quant_config: FusedMoEQuantConfig,
209
- out_dtype: torch.dtype | None = None,
210
- use_deepseek_fp8_block_scale: bool = False,
211
-) -> mk.FusedMoEPermuteExpertsUnpermute:
212
- """Return a GEMM *experts* implementation for fused-MoE layers"""
213
-
214
- if moe is not None:
215
- return FlashInferExperts(
216
- out_dtype=moe.in_dtype,
217
- quant_config=quant_config,
218
- ep_rank=moe.moe_parallel_config.ep_rank,
219
- ep_size=moe.moe_parallel_config.ep_size,
220
- tp_rank=moe.moe_parallel_config.tp_rank,
221
- tp_size=moe.moe_parallel_config.tp_size,
222
- use_deepseek_fp8_block_scale=use_deepseek_fp8_block_scale,
223
- )
224
-
225
- assert out_dtype is not None, "If moe config is None, out_dtype must be passed"
226
- return FlashInferExperts(
227
- out_dtype=out_dtype,
228
- quant_config=quant_config,
229
- use_deepseek_fp8_block_scale=use_deepseek_fp8_block_scale,
230
- )
231
-
232
-
202
def get_flashinfer_moe_backend() -> FlashinferMoeBackend:
203
backend_map = {
204
"throughput": FlashinferMoeBackend.CUTLASS,
vllm/model_executor/layers/quantization/utils/quant_utils.py
+26
-12
@@ -48,6 +48,7 @@ class GroupShape(_GroupShape):
48
# Aliases for common quantization group shapes
49
PER_TENSOR: ClassVar["GroupShape"]
50
PER_TOKEN: ClassVar["GroupShape"]
51
+ PER_CHANNEL: ClassVar["GroupShape"]
52
53
def is_per_tensor(self) -> bool:
54
return self.row == -1 and self.col == -1
@@ -55,12 +56,16 @@ class GroupShape(_GroupShape):
56
def is_per_token(self) -> bool:
57
return self.row == 1 and self.col == -1
58
59
+ def is_per_channel(self) -> bool:
60
+ return self.row == -1 and self.col == 1
61
+
62
def is_per_group(self) -> bool:
63
return self.row == 1 and self.col >= 1
64
65
66
GroupShape.PER_TENSOR = GroupShape(-1, -1)
67
GroupShape.PER_TOKEN = GroupShape(1, -1)
68
+GroupShape.PER_CHANNEL = GroupShape(-1, 1)
69
70
71
@dataclass(frozen=True)
@@ -77,16 +82,12 @@ class ScaleDesc:
82
group_shape: GroupShape
83
84
def __str__(self):
80
- group_shape = (
81
- "per_tensor"
82
- if self.group_shape == GroupShape.PER_TENSOR
83
- else (
84
- "per_token"
85
- if self.group_shape == GroupShape.PER_TOKEN
86
- else str(self.group_shape)
87
- )
88
- )
89
-
85
+ d = {
86
+ GroupShape.PER_TENSOR: "per_tensor",
87
+ GroupShape.PER_TOKEN: "per_token",
88
+ GroupShape.PER_CHANNEL: "per_channel",
89
+ }
90
+ group_shape = d.get(self.group_shape, str(self.group_shape))
91
return (
92
f"{fx.graph.dtype_abbrs[self.dtype]},"
93
f"{'static' if self.static else 'dynamic'},{group_shape}"
@@ -126,15 +127,28 @@ kFp8DynamicTensorSym = QuantKey(FP8_DTYPE, kDynamicTensorScale, symmetric=True)
127
kStaticTokenScale = ScaleDesc(torch.float32, True, GroupShape.PER_TOKEN)
128
kFp8StaticTokenSym = QuantKey(FP8_DTYPE, kStaticTokenScale, symmetric=True)
129
130
+kStaticChannelScale = ScaleDesc(torch.float32, True, GroupShape.PER_CHANNEL)
131
+kFp8StaticChannelSym = QuantKey(FP8_DTYPE, kStaticChannelScale, symmetric=True)
132
+
133
kDynamicTokenScale = ScaleDesc(torch.float32, False, GroupShape.PER_TOKEN)
134
kFp8DynamicTokenSym = QuantKey(FP8_DTYPE, kDynamicTokenScale, symmetric=True)
135
132
-kNvfp4GroupScale = ScaleDesc(FP8_DTYPE, False, GroupShape(1, 16))
133
-kNvfp4Quant = QuantKey(FP4_DTYPE, scale=kNvfp4GroupScale, scale2=kStaticTensorScale)
136
+kNvfp4DynamicGroupScale = ScaleDesc(FP8_DTYPE, False, GroupShape(1, 16))
137
+kNvfp4Dynamic = QuantKey(
138
+ FP4_DTYPE, scale=kNvfp4DynamicGroupScale, scale2=kStaticTensorScale
139
+)
140
+
141
+kNvfp4StaticGroupScale = ScaleDesc(FP8_DTYPE, True, GroupShape(1, 16))
142
+kNvfp4Static = QuantKey(
143
+ FP4_DTYPE, scale=kNvfp4StaticGroupScale, scale2=kStaticTensorScale
144
+)
145
146
kDynamic128Scale = ScaleDesc(torch.float32, False, GroupShape(1, 128))
147
kFp8Dynamic128Sym = QuantKey(FP8_DTYPE, kDynamic128Scale, symmetric=True)
148
149
+kStatic128BlockScale = ScaleDesc(torch.float32, True, GroupShape(128, 128))
150
+kFp8Static128BlockSym = QuantKey(FP8_DTYPE, kStatic128BlockScale, symmetric=True)
151
+
152
kDynamic64Scale = ScaleDesc(torch.float32, False, GroupShape(1, 64))
153
kFp8Dynamic64Sym = QuantKey(FP8_DTYPE, kDynamic64Scale, symmetric=True)
154
vllm/model_executor/models/qwen3_moe.py
-2
@@ -43,7 +43,6 @@ from vllm.distributed import (
43
from vllm.logger import init_logger
44
from vllm.model_executor.layers.activation import SiluAndMul
45
from vllm.model_executor.layers.fused_moe import FusedMoE
46
-from vllm.model_executor.layers.fused_moe.config import RoutingMethodType
46
from vllm.model_executor.layers.layernorm import RMSNorm
47
from vllm.model_executor.layers.linear import (
48
MergedColumnParallelLinear,
@@ -172,7 +171,6 @@ class Qwen3MoeSparseMoeBlock(nn.Module):
171
enable_eplb=self.enable_eplb,
172
num_redundant_experts=self.n_redundant_experts,
173
is_sequence_parallel=self.is_sequence_parallel,
175
- routing_method_type=RoutingMethodType.Renormalize,
174
)
175
176
self.gate = ReplicatedLinear(
vllm/model_executor/models/qwen3_next.py
-2
@@ -34,7 +34,6 @@ from vllm.model_executor.layers.fla.ops import (
34
fused_recurrent_gated_delta_rule,
35
)
36
from vllm.model_executor.layers.fused_moe import SharedFusedMoE
37
-from vllm.model_executor.layers.fused_moe.config import RoutingMethodType
37
from vllm.model_executor.layers.layernorm import (
38
GemmaRMSNorm as Qwen3NextRMSNorm,
39
)
@@ -181,7 +180,6 @@ class Qwen3NextSparseMoeBlock(nn.Module):
180
enable_eplb=self.enable_eplb,
181
num_redundant_experts=self.n_redundant_experts,
182
is_sequence_parallel=self.is_sequence_parallel,
184
- routing_method_type=RoutingMethodType.Renormalize,
183
)
184
185
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
vllm/model_executor/warmup/deep_gemm_warmup.py
+4
@@ -128,11 +128,15 @@ def _fp8_linear_may_use_deep_gemm(module: torch.nn.Module) -> bool:
128
"""
129
Return True if the input module/layer could be processed with DeepGEMM.
130
"""
131
+
132
+ # FIXME: this logic is brittle and incorrect - since we
133
+ # could use DeepGEMM with for than just Fp8LinearMethod
134
block_size = get_mk_alignment_for_contiguous_layout()[0]
135
if not (
136
isinstance(module, LinearBase)
137
and isinstance(module.quant_method, Fp8LinearMethod)
138
and module.quant_method.block_quant
139
+ and not module.quant_method.use_marlin
140
):
141
return False
142
vllm/v1/attention/backends/flashinfer.py
+2
-2
@@ -29,7 +29,7 @@ from vllm.model_executor.layers.batch_invariant import (
29
from vllm.model_executor.layers.quantization.utils.quant_utils import (
30
QuantKey,
31
kFp8StaticTensorSym,
32
- kNvfp4Quant,
32
+ kNvfp4Dynamic,
33
)
34
from vllm.platforms import current_platform
35
from vllm.platforms.interface import DeviceCapability
@@ -1184,7 +1184,7 @@ class FlashInferImpl(AttentionImpl):
1184
return (
1185
self.support_trtllm_attn
1186
and self.kv_cache_dtype.startswith("fp8")
1187
- and quant_key in (kFp8StaticTensorSym, kNvfp4Quant)
1187
+ and quant_key in (kFp8StaticTensorSym, kNvfp4Dynamic)
1188
)
1189
1190
# FlashInfer requires attention sinks to be float32