@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / de527e1cec
Fork 0

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 7, 17:45 UTC

[UX] Add `--moe-backend` arg for explicit kernel selection (#33807)

Signed-off-by: mgoin <mgoin64@gmail.com> Co-authored-by: Robert Shaw <114415538+robertgshaw2-redhat@users.noreply.github.com>

Michael Goin committed Feb 25, 2026 at 20:44 UTC de527e1cec820686f2bead759e4e99a20b172589
37 files changed +260 -140
tests/evals/gsm8k/configs/Qwen3-Next-80B-A3B-NVFP4-EP2.yaml
+1 -2
@@ -8,5 +8,4 @@ server_args: >-
8 --tensor-parallel-size 2
9 --enable-expert-parallel
10 --speculative-config '{"method":"qwen3_next_mtp","num_speculative_tokens":1}'
11 -env:
12 - VLLM_USE_FLASHINFER_MOE_FP4: "1"
11 + --moe-backend=flashinfer_trtllm
tests/evals/gsm8k/configs/Qwen3-Next-FP8-EP2.yaml
+1 -2
@@ -7,5 +7,4 @@ server_args: >-
7 --tensor-parallel-size 2
8 --enable-expert-parallel
9 --async-scheduling
10 -env:
11 - VLLM_USE_FLASHINFER_MOE_FP8: "1"
10 + --moe-backend=flashinfer_trtllm
tests/evals/gsm8k/configs/moe-refactor-dp-ep/Llama-4-Scout-Fp8-ModelOpt-triton.yaml
+1 -2
@@ -2,7 +2,6 @@ model_name: "nvidia/Llama-4-Scout-17B-16E-Instruct-FP8"
2 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"
5 +server_args: "--enforce-eager --max-model-len 8192 --data-parallel-size 2 --enable-expert-parallel --moe-backend=triton"
6 env:
7 - VLLM_USE_FLASHINFER_MOE_FP8: "0"
7 VLLM_USE_DEEP_GEMM: "0"
tests/evals/gsm8k/configs/moe-refactor-dp-ep/Qwen3-30B-A3B-NvFp4-CT-fi-cutedsl-deepep-ll.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "RedHatAI/Qwen3-30B-A3B-NVFP4"
2 accuracy_threshold: 0.88
3 num_questions: 1319
4 num_fewshot: 5
5 -server_args: "--enforce-eager --max-model-len 8192 --data-parallel-size 2 --enable-expert-parallel --all2all-backend deepep_low_latency"
6 -env:
7 - VLLM_USE_FLASHINFER_MOE_FP4: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "masked_gemm"
5 +server_args: "--enforce-eager --max-model-len 8192 --data-parallel-size 2 --enable-expert-parallel --all2all-backend deepep_low_latency --moe-backend=flashinfer_cutedsl"
tests/evals/gsm8k/configs/moe-refactor-dp-ep/Qwen3-30B-A3B-NvFp4-CT-fi-cutlass.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "RedHatAI/Qwen3-30B-A3B-NVFP4"
2 accuracy_threshold: 0.88
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_FP4: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "throughput"
5 +server_args: "--enforce-eager --max-model-len 8192 --data-parallel-size 2 --enable-expert-parallel --moe-backend=flashinfer_cutlass"
tests/evals/gsm8k/configs/moe-refactor-dp-ep/Qwen3-30B-A3B-NvFp4-ModelOpt-fi-cutedsl-deepep-ll.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "nvidia/Qwen3-30B-A3B-NVFP4"
2 accuracy_threshold: 0.88
3 num_questions: 1319
4 num_fewshot: 5
5 -server_args: "--enforce-eager --max-model-len 8192 --data-parallel-size 2 --enable-expert-parallel --all2all-backend deepep_low_latency"
6 -env:
7 - VLLM_USE_FLASHINFER_MOE_FP4: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "masked_gemm"
5 +server_args: "--enforce-eager --max-model-len 8192 --data-parallel-size 2 --enable-expert-parallel --all2all-backend deepep_low_latency --moe-backend=flashinfer_cutedsl"
tests/evals/gsm8k/configs/moe-refactor-dp-ep/Qwen3-30B-A3B-NvFp4-ModelOpt-fi-cutlass.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "nvidia/Qwen3-30B-A3B-NVFP4"
2 accuracy_threshold: 0.88
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_FP4: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "throughput"
5 +server_args: "--enforce-eager --max-model-len 8192 --data-parallel-size 2 --enable-expert-parallel --moe-backend=flashinfer_cutlass"
tests/evals/gsm8k/configs/moe-refactor-dp-ep/Qwen3-30B-A3B-NvFp4-ModelOpt-fi-trtllm.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "nvidia/Qwen3-30B-A3B-NVFP4"
2 accuracy_threshold: 0.88
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_FP4: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "latency"
5 +server_args: "--enforce-eager --max-model-len 8192 --data-parallel-size 2 --enable-expert-parallel --moe-backend=flashinfer_trtllm"
tests/evals/gsm8k/configs/moe-refactor/Llama-4-Scout-BF16-fi-cutlass.yaml
+1 -5
@@ -2,8 +2,4 @@ model_name: "meta-llama/Llama-4-Scout-17B-16E-Instruct"
2 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 --enable-expert-parallel"
6 -env:
7 - VLLM_USE_FLASHINFER_MOE_FP16: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "throughput"
9 -
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --enable-expert-parallel --moe-backend=flashinfer_cutlass"
tests/evals/gsm8k/configs/moe-refactor/Llama-4-Scout-Fp8-ModelOpt-fi-cutlass.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "nvidia/Llama-4-Scout-17B-16E-Instruct-FP8"
2 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: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "throughput"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=flashinfer_cutlass"
tests/evals/gsm8k/configs/moe-refactor/Llama-4-Scout-Fp8-ModelOpt-fi-trtllm.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "nvidia/Llama-4-Scout-17B-16E-Instruct-FP8"
2 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: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "latency"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=flashinfer_trtllm"
tests/evals/gsm8k/configs/moe-refactor/Llama-4-Scout-Fp8-ModelOpt-triton.yaml
+1 -3
@@ -2,6 +2,4 @@ model_name: "nvidia/Llama-4-Scout-17B-16E-Instruct-FP8"
2 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"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=triton"
tests/evals/gsm8k/configs/moe-refactor/Mixtral-8x7B-BF16-fi-cutlass.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "mistralai/Mixtral-8x7B-v0.1"
2 accuracy_threshold: 0.58
3 num_questions: 1319
4 num_fewshot: 5
5 -server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --enable-expert-parallel"
6 -env:
7 - VLLM_USE_FLASHINFER_MOE_FP16: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "throughput"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --enable-expert-parallel --moe-backend=flashinfer_cutlass"
tests/evals/gsm8k/configs/moe-refactor/Mixtral-8x7B-Fp8-AutoFp8-fi-cutlass.yaml
+1 -4
@@ -3,7 +3,4 @@
3 # accuracy_threshold: 0.62
4 # num_questions: 1319
5 # num_fewshot: 5
6 -# server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2"
7 -# env:
8 -# VLLM_USE_FLASHINFER_MOE_FP8: "1"
9 -# VLLM_FLASHINFER_MOE_BACKEND: "throughput"
6 +# server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=flashinfer_cutlass"
tests/evals/gsm8k/configs/moe-refactor/Nemotron-Nano-30B-Fp8-ModelOpt-fi-trtllm.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-FP8"
2 accuracy_threshold: 0.29
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: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "latency"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=flashinfer_trtllm"
tests/evals/gsm8k/configs/moe-refactor/Nemotron-Nano-30B-NvFp4-ModelOpt-fi-cutlass.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-NVFP4"
2 accuracy_threshold: 0.29
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: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "throughput"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=flashinfer_cutlass"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-BF16-fi-cutlass.yaml
+1 -3
@@ -2,6 +2,4 @@ model_name: "Qwen/Qwen3-30B-A3B"
2 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 --enable-expert-parallel"
6 -env:
7 - VLLM_USE_FLASHINFER_MOE_FP16: "1"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --enable-expert-parallel --moe-backend=flashinfer_cutlass"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-Fp8-AutoFp8-fi-cutlass.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "Qwen/Qwen3-Coder-30B-A3B-Instruct-FP8"
2 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_FP8: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "throughput"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=flashinfer_cutlass"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-Fp8-AutoFp8-fi-trtllm.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "Qwen/Qwen3-Coder-30B-A3B-Instruct-FP8"
2 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_FP8: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "latency"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=flashinfer_trtllm"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-Fp8-AutoFp8-triton.yaml
+1 -2
@@ -2,7 +2,6 @@ model_name: "Qwen/Qwen3-Coder-30B-A3B-Instruct-FP8"
2 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"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=triton"
6 env:
7 - VLLM_USE_FLASHINFER_MOE_FP8: "0"
7 VLLM_USE_DEEP_GEMM: "0"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-Fp8-CT-Block-fi-cutlass.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "RedHatAI/Qwen3-30B-A3B-FP8-block"
2 accuracy_threshold: 0.85
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: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "throughput"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=flashinfer_cutlass"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-Fp8-CT-Block-triton.yaml
+1 -2
@@ -2,7 +2,6 @@ model_name: "RedHatAI/Qwen3-30B-A3B-FP8-block"
2 accuracy_threshold: 0.85
3 num_questions: 1319
4 num_fewshot: 5
5 -server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=triton"
6 env:
7 - VLLM_USE_FLASHINFER_MOE_FP8: "0"
7 VLLM_USE_DEEP_GEMM: "0"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-NvFp4-CT-fi-cutlass.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "RedHatAI/Qwen3-30B-A3B-NVFP4"
2 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: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "throughput"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=flashinfer_cutlass"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-NvFp4-CT-fi-trtllm.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "RedHatAI/Qwen3-30B-A3B-NVFP4"
2 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: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "latency"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=flashinfer_trtllm"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-NvFp4-CT-vllm-cutlass.yaml
+1 -3
@@ -2,6 +2,4 @@ model_name: "RedHatAI/Qwen3-30B-A3B-NVFP4"
2 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"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=cutlass"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-NvFp4-ModelOpt-fi-cutlass.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "nvidia/Qwen3-30B-A3B-NVFP4"
2 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: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "throughput"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=flashinfer_cutlass"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-NvFp4-ModelOpt-fi-trtllm.yaml
+1 -4
@@ -2,7 +2,4 @@ model_name: "nvidia/Qwen3-30B-A3B-NVFP4"
2 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: "1"
8 - VLLM_FLASHINFER_MOE_BACKEND: "latency"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=flashinfer_trtllm"
tests/evals/gsm8k/configs/moe-refactor/Qwen3-30B-A3B-NvFp4-ModelOpt-vllm-cutlass.yaml
+1 -3
@@ -2,6 +2,4 @@ model_name: "nvidia/Qwen3-30B-A3B-NVFP4"
2 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"
5 +server_args: "--enforce-eager --max-model-len 8192 --tensor-parallel-size 2 --moe-backend=cutlass"
tests/quantization/test_blackwell_moe.py
+42 -28
@@ -85,34 +85,34 @@ def can_initialize(
85 )
86 )
87 def test_llama4_fp8_tensor_moe_flashinfer_cutlass(monkeypatch: pytest.MonkeyPatch):
88 - monkeypatch.setenv("VLLM_USE_FLASHINFER_MOE_FP8", "1")
89 - monkeypatch.setenv("VLLM_FLASHINFER_MOE_BACKEND", "throughput")
88 can_initialize(
91 - "nvidia/Llama-4-Scout-17B-16E-Instruct-FP8", hf_overrides=HF_OVERRIDE_MM
89 + "nvidia/Llama-4-Scout-17B-16E-Instruct-FP8",
90 + hf_overrides=HF_OVERRIDE_MM,
91 + extra_args=["--moe-backend=flashinfer_cutlass"],
92 )
93
94
95 def test_llama4_fp8_tensor_moe_flashinfer_trtllm(monkeypatch: pytest.MonkeyPatch):
96 - monkeypatch.setenv("VLLM_USE_FLASHINFER_MOE_FP8", "1")
97 - monkeypatch.setenv("VLLM_FLASHINFER_MOE_BACKEND", "latency")
96 can_initialize(
99 - "nvidia/Llama-4-Scout-17B-16E-Instruct-FP8", hf_overrides=HF_OVERRIDE_MM
97 + "nvidia/Llama-4-Scout-17B-16E-Instruct-FP8",
98 + hf_overrides=HF_OVERRIDE_MM,
99 + extra_args=["--moe-backend=flashinfer_trtllm"],
100 )
101
102
103 def test_llama4_nvfp4_moe_flashinfer_cutlass(monkeypatch: pytest.MonkeyPatch):
104 - monkeypatch.setenv("VLLM_USE_FLASHINFER_MOE_FP4", "1")
105 - monkeypatch.setenv("VLLM_FLASHINFER_MOE_BACKEND", "throughput")
104 can_initialize(
107 - "nvidia/Llama-4-Scout-17B-16E-Instruct-FP4", hf_overrides=HF_OVERRIDE_MM
105 + "nvidia/Llama-4-Scout-17B-16E-Instruct-FP4",
106 + hf_overrides=HF_OVERRIDE_MM,
107 + extra_args=["--moe-backend=flashinfer_cutlass"],
108 )
109
110
111 def test_llama4_nvfp4_moe_flashinfer_trtllm(monkeypatch: pytest.MonkeyPatch):
112 - monkeypatch.setenv("VLLM_USE_FLASHINFER_MOE_FP4", "1")
113 - monkeypatch.setenv("VLLM_FLASHINFER_MOE_BACKEND", "latency")
112 can_initialize(
115 - "nvidia/Llama-4-Scout-17B-16E-Instruct-FP4", hf_overrides=HF_OVERRIDE_MM
113 + "nvidia/Llama-4-Scout-17B-16E-Instruct-FP4",
114 + hf_overrides=HF_OVERRIDE_MM,
115 + extra_args=["--moe-backend=flashinfer_trtllm"],
116 )
117
118
@@ -120,8 +120,11 @@ def test_llama4_nvfp4_moe_flashinfer_trtllm(monkeypatch: pytest.MonkeyPatch):
120
121
122 def test_deepseek_fp8_block_moe_deep_gemm(monkeypatch: pytest.MonkeyPatch):
123 - monkeypatch.setenv("VLLM_USE_DEEP_GEMM", "1")
124 - can_initialize("deepseek-ai/DeepSeek-V3.1", hf_overrides=HF_OVERRIDE_TEXT)
123 + can_initialize(
124 + "deepseek-ai/DeepSeek-V3.1",
125 + hf_overrides=HF_OVERRIDE_TEXT,
126 + extra_args=["--moe-backend=deep_gemm"],
127 + )
128
129
130 @pytest.mark.skip(
@@ -131,27 +134,35 @@ def test_deepseek_fp8_block_moe_deep_gemm(monkeypatch: pytest.MonkeyPatch):
134 )
135 )
136 def test_deepseek_fp8_block_moe_flashinfer_cutlass(monkeypatch: pytest.MonkeyPatch):
134 - monkeypatch.setenv("VLLM_USE_FLASHINFER_MOE_FP8", "1")
135 - monkeypatch.setenv("VLLM_FLASHINFER_MOE_BACKEND", "throughput")
136 - can_initialize("deepseek-ai/DeepSeek-V3.1", hf_overrides=HF_OVERRIDE_TEXT)
137 + can_initialize(
138 + "deepseek-ai/DeepSeek-V3.1",
139 + hf_overrides=HF_OVERRIDE_TEXT,
140 + extra_args=["--moe-backend=flashinfer_cutlass"],
141 + )
142
143
144 def test_deepseek_fp8_block_moe_flashinfer_trtllm(monkeypatch: pytest.MonkeyPatch):
140 - monkeypatch.setenv("VLLM_USE_FLASHINFER_MOE_FP8", "1")
141 - monkeypatch.setenv("VLLM_FLASHINFER_MOE_BACKEND", "latency")
142 - can_initialize("deepseek-ai/DeepSeek-V3.1", hf_overrides=HF_OVERRIDE_TEXT)
145 + can_initialize(
146 + "deepseek-ai/DeepSeek-V3.1",
147 + hf_overrides=HF_OVERRIDE_TEXT,
148 + extra_args=["--moe-backend=flashinfer_trtllm"],
149 + )
150
151
152 def test_deepseek_nvfp4_moe_flashinfer_cutlass(monkeypatch: pytest.MonkeyPatch):
146 - monkeypatch.setenv("VLLM_USE_FLASHINFER_MOE_FP4", "1")
147 - monkeypatch.setenv("VLLM_FLASHINFER_MOE_BACKEND", "throughput")
148 - can_initialize("nvidia/DeepSeek-R1-0528-FP4-v2", hf_overrides=HF_OVERRIDE_TEXT)
153 + can_initialize(
154 + "nvidia/DeepSeek-R1-0528-FP4-v2",
155 + hf_overrides=HF_OVERRIDE_TEXT,
156 + extra_args=["--moe-backend=flashinfer_cutlass"],
157 + )
158
159
160 def test_deepseek_nvfp4_moe_flashinfer_trtllm(monkeypatch: pytest.MonkeyPatch):
152 - monkeypatch.setenv("VLLM_USE_FLASHINFER_MOE_FP4", "1")
153 - monkeypatch.setenv("VLLM_FLASHINFER_MOE_BACKEND", "latency")
154 - can_initialize("nvidia/DeepSeek-R1-0528-FP4-v2", hf_overrides=HF_OVERRIDE_TEXT)
161 + can_initialize(
162 + "nvidia/DeepSeek-R1-0528-FP4-v2",
163 + hf_overrides=HF_OVERRIDE_TEXT,
164 + extra_args=["--moe-backend=flashinfer_trtllm"],
165 + )
166
167
168 ## GPT-OSS ##
@@ -184,5 +195,8 @@ def test_gptoss_eager(monkeypatch: pytest.MonkeyPatch):
195
196
197 def test_qwen3_next_bf16_moe_flashinfer_trtllm(monkeypatch: pytest.MonkeyPatch):
187 - monkeypatch.setenv("VLLM_USE_FLASHINFER_MOE_FP16", "1")
188 - can_initialize("Qwen/Qwen3-Next-80B-A3B-Instruct", hf_overrides=HF_OVERRIDE_TEXT)
198 + can_initialize(
199 + "Qwen/Qwen3-Next-80B-A3B-Instruct",
200 + hf_overrides=HF_OVERRIDE_TEXT,
201 + extra_args=["--moe-backend=flashinfer_trtllm"],
202 + )
vllm/config/kernel.py
+33 -1
@@ -2,13 +2,25 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Callable
5 -from typing import Any
5 +from typing import Any, Literal
6
7 from pydantic import Field, field_validator
8
9 from vllm.config.utils import config
10 from vllm.utils.hashing import safe_hash
11
12 +MoEBackend = Literal[
13 + "auto",
14 + "triton",
15 + "deep_gemm",
16 + "cutlass",
17 + "flashinfer_trtllm",
18 + "flashinfer_cutlass",
19 + "flashinfer_cutedsl",
20 + "marlin",
21 + "aiter",
22 +]
23 +
24
25 @config
26 class KernelConfig:
@@ -17,6 +29,26 @@ class KernelConfig:
29 enable_flashinfer_autotune: bool = Field(default=None)
30 """If True, run FlashInfer autotuning during kernel warmup."""
31
32 + moe_backend: MoEBackend = "auto"
33 + """Backend for MoE expert computation kernels. Available options:
34 +
35 + - "auto": Automatically select the best backend based on model and hardware\n
36 + - "triton": Use Triton-based fused MoE kernels\n
37 + - "deep_gemm": Use DeepGEMM kernels (FP8 block-quantized only)\n
38 + - "cutlass": Use vLLM CUTLASS kernels\n
39 + - "flashinfer_trtllm": Use FlashInfer with TRTLLM-GEN kernels\n
40 + - "flashinfer_cutlass": Use FlashInfer with CUTLASS kernels\n
41 + - "flashinfer_cutedsl": Use FlashInfer with CuteDSL kernels (FP4 only)\n
42 + - "marlin": Use Marlin kernels (weight-only quantization)\n
43 + - "aiter": Use AMD AITer kernels (ROCm only)"""
44 +
45 + @field_validator("moe_backend", mode="before")
46 + @classmethod
47 + def _normalize_moe_backend(cls, value: Any) -> Any:
48 + if isinstance(value, str):
49 + return value.lower().replace("-", "_")
50 + return value
51 +
52 def compute_hash(self) -> str:
53 """
54 WARNING: Whenever a new field is added to this config,
vllm/engine/arg_utils.py
+7
@@ -70,6 +70,7 @@ from vllm.config.cache import (
70 PrefixCachingHashAlgo,
71 )
72 from vllm.config.device import Device
73 +from vllm.config.kernel import MoEBackend
74 from vllm.config.lora import MaxLoRARanks
75 from vllm.config.model import (
76 ConvertOption,
@@ -416,6 +417,7 @@ class EngineArgs:
417 data_parallel_external_lb: bool = False
418 data_parallel_backend: DataParallelBackend = ParallelConfig.data_parallel_backend
419 enable_expert_parallel: bool = ParallelConfig.enable_expert_parallel
420 + moe_backend: MoEBackend = KernelConfig.moe_backend
421 all2all_backend: All2AllBackend = ParallelConfig.all2all_backend
422 enable_dbo: bool = ParallelConfig.enable_dbo
423 ubatch_size: int = ParallelConfig.ubatch_size
@@ -1227,6 +1229,9 @@ class EngineArgs:
1229 "--enable-flashinfer-autotune",
1230 **kernel_kwargs["enable_flashinfer_autotune"],
1231 )
1232 + moe_backend_kwargs = kernel_kwargs["moe_backend"]
1233 + moe_backend_kwargs["type"] = lambda s: s.lower().replace("-", "_")
1234 + kernel_group.add_argument("--moe-backend", **moe_backend_kwargs)
1235
1236 # vLLM arguments
1237 vllm_kwargs = get_kwargs(VllmConfig)
@@ -1817,6 +1822,8 @@ class EngineArgs:
1822 "are mutually exclusive"
1823 )
1824 kernel_config.enable_flashinfer_autotune = self.enable_flashinfer_autotune
1825 + if self.moe_backend != "auto":
1826 + kernel_config.moe_backend = self.moe_backend
1827
1828 load_config = self.create_load_config()
1829
vllm/model_executor/layers/fused_moe/config.py
+1 -1
@@ -1066,7 +1066,6 @@ class FusedMoEParallelConfig:
1066 - Comment: There are 2 engine instances and the experts are split
1067 between the 4 devices.
1068 """
1069 -
1069 use_ep = (
1070 dp_size_ * pcp_size_ * tp_size_ > 1
1071 and vllm_parallel_config.enable_expert_parallel
@@ -1155,6 +1154,7 @@ class FusedMoEConfig:
1154 # Defaults to in_dtype if not specified.
1155 router_logits_dtype: torch.dtype | None = None
1156
1157 + moe_backend: str = "auto"
1158 max_num_tokens: int = envs.VLLM_MOE_DP_CHUNK_SIZE
1159 has_bias: bool = False
1160 is_act_and_mul: bool = True
vllm/model_executor/layers/fused_moe/deepep_ll_prepare_finalize.py
-1
@@ -198,7 +198,6 @@ class DeepEPLLPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
198 x = x[0].permute(2, 0, 1)
199 num_experts, max_tokens, hidden_dim_by_2 = x.shape
200 hidden_dim = hidden_dim_by_2 * 2
201 - assert envs.VLLM_FLASHINFER_MOE_BACKEND == "masked_gemm"
201 logger.info_once(
202 "Quantization is fused with DeepEP nvfp4 dispatch for "
203 "FlashInfer CUTEDSL as VLLM_DEEPEPLL_NVFP4_DISPATCH==1"
vllm/model_executor/layers/fused_moe/layer.py
+1
@@ -550,6 +550,7 @@ class FusedMoE(CustomOp):
550 num_logical_experts=self.logical_num_experts,
551 moe_parallel_config=self.moe_parallel_config,
552 in_dtype=moe_in_dtype,
553 + moe_backend=vllm_config.kernel_config.moe_backend,
554 router_logits_dtype=router_logits_dtype,
555 max_num_tokens=envs.VLLM_MOE_DP_CHUNK_SIZE,
556 has_bias=has_bias,
vllm/model_executor/layers/fused_moe/oracle/fp8.py
+59
@@ -7,6 +7,7 @@ import torch
7 import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8 from vllm import envs
9 from vllm._aiter_ops import rocm_aiter_ops
10 +from vllm.config.kernel import MoEBackend
11 from vllm.logger import init_logger
12 from vllm.model_executor.layers.fused_moe.all2all_utils import (
13 maybe_make_prepare_finalize,
@@ -180,6 +181,25 @@ def backend_to_kernel_cls(
181 raise ValueError(f"Unknown FP8 MoE backend: {backend.value}")
182
183
184 +def map_fp8_backend(runner_backend: MoEBackend) -> Fp8MoeBackend:
185 + """Map user's MoEBackend to Fp8MoeBackend."""
186 + mapping = {
187 + "triton": Fp8MoeBackend.TRITON,
188 + "deep_gemm": Fp8MoeBackend.DEEPGEMM,
189 + "cutlass": Fp8MoeBackend.VLLM_CUTLASS,
190 + "flashinfer_trtllm": Fp8MoeBackend.FLASHINFER_TRTLLM,
191 + "flashinfer_cutlass": Fp8MoeBackend.FLASHINFER_CUTLASS,
192 + "marlin": Fp8MoeBackend.MARLIN,
193 + "aiter": Fp8MoeBackend.AITER,
194 + }
195 + if backend := mapping.get(runner_backend):
196 + return backend
197 + raise ValueError(
198 + f"moe_backend='{runner_backend}' is not supported for FP8 MoE. "
199 + f"Expected one of {list(mapping.keys())}."
200 + )
201 +
202 +
203 def select_fp8_moe_backend(
204 config: FusedMoEConfig,
205 weight_key: QuantKey | None,
@@ -242,6 +262,45 @@ def select_fp8_moe_backend(
262 return backend, k_cls
263 raise ValueError(_make_log_unsupported(backend, reason))
264
265 + # Handle explicit moe_backend from user.
266 + runner_backend = config.moe_backend
267 + if runner_backend != "auto":
268 + requested_backend = map_fp8_backend(runner_backend)
269 + # For batched activation format, use batched variants if available.
270 + if activation_format == mk.FusedMoEActivationFormat.BatchedExperts:
271 + if requested_backend == Fp8MoeBackend.DEEPGEMM:
272 + requested_backend = Fp8MoeBackend.BATCHED_DEEPGEMM
273 + elif requested_backend == Fp8MoeBackend.TRITON:
274 + requested_backend = Fp8MoeBackend.BATCHED_TRITON
275 + elif requested_backend == Fp8MoeBackend.VLLM_CUTLASS:
276 + requested_backend = Fp8MoeBackend.BATCHED_VLLM_CUTLASS
277 +
278 + if (
279 + requested_backend
280 + in [
281 + Fp8MoeBackend.VLLM_CUTLASS,
282 + Fp8MoeBackend.BATCHED_VLLM_CUTLASS,
283 + ]
284 + and not allow_vllm_cutlass
285 + ):
286 + raise ValueError(
287 + "vLLM CUTLASS FP8 MoE backend is disabled for this configuration."
288 + )
289 +
290 + # Handle FLASHINFER_TRTLLM specially (no kernel class).
291 + if requested_backend == Fp8MoeBackend.FLASHINFER_TRTLLM:
292 + supported, reason = is_supported_config_trtllm_fp8(
293 + config, weight_key, activation_key, activation_format
294 + )
295 + if supported:
296 + logger.info_once(_make_log_backend(requested_backend))
297 + return requested_backend, None
298 + raise ValueError(_make_log_unsupported(requested_backend, reason))
299 +
300 + return _return_or_raise(
301 + requested_backend, config, weight_key, activation_key, activation_format
302 + )
303 +
304 # Handle explicit FlashInfer FP8 configuration.
305 if envs.is_set("VLLM_USE_FLASHINFER_MOE_FP8"):
306 if not envs.VLLM_USE_FLASHINFER_MOE_FP8:
vllm/model_executor/layers/fused_moe/oracle/nvfp4.py
+35
@@ -6,6 +6,7 @@ import torch
6
7 import vllm.envs as envs
8 import vllm.model_executor.layers.fused_moe.modular_kernel as mk
9 +from vllm.config.kernel import MoEBackend
10 from vllm.logger import init_logger
11 from vllm.model_executor.layers.fused_moe.all2all_utils import (
12 maybe_make_prepare_finalize,
@@ -103,6 +104,23 @@ def backend_to_kernel_cls(
104 raise ValueError(f"Unknown NvFP4 MoE backend: {backend.value}")
105
106
107 +def map_nvfp4_backend(runner_backend: MoEBackend) -> NvFp4MoeBackend:
108 + """Map user's MoEBackend to NvFp4MoeBackend."""
109 + mapping = {
110 + "cutlass": NvFp4MoeBackend.VLLM_CUTLASS,
111 + "flashinfer_trtllm": NvFp4MoeBackend.FLASHINFER_TRTLLM,
112 + "flashinfer_cutlass": NvFp4MoeBackend.FLASHINFER_CUTLASS,
113 + "flashinfer_cutedsl": NvFp4MoeBackend.FLASHINFER_CUTEDSL,
114 + "marlin": NvFp4MoeBackend.MARLIN,
115 + }
116 + if backend := mapping.get(runner_backend):
117 + return backend
118 + raise ValueError(
119 + f"moe_backend='{runner_backend}' is not supported for NvFP4 MoE. "
120 + f"Expected one of {list(mapping.keys())}."
121 + )
122 +
123 +
124 def select_nvfp4_moe_backend(
125 config: FusedMoEConfig,
126 weight_key: QuantKey | None,
@@ -170,6 +188,23 @@ def select_nvfp4_moe_backend(
188 return backend, k_cls
189 raise ValueError(_make_log_unsupported(backend, reason))
190
191 + # Handle explicit moe_backend from user.
192 + runner_backend = config.moe_backend
193 + if runner_backend != "auto":
194 + requested_backend = map_nvfp4_backend(runner_backend)
195 + if requested_backend == NvFp4MoeBackend.FLASHINFER_TRTLLM:
196 + supported, reason = is_supported_config_trtllm(
197 + config, weight_key, activation_key, activation_format
198 + )
199 + if supported:
200 + logger.info_once(_make_log_backend(requested_backend))
201 + return requested_backend, None
202 + raise ValueError(_make_log_unsupported(requested_backend, reason))
203 +
204 + return _return_or_raise(
205 + requested_backend, config, weight_key, activation_key, activation_format
206 + )
207 +
208 if envs.is_set("VLLM_USE_FLASHINFER_MOE_FP4"):
209 if not envs.VLLM_USE_FLASHINFER_MOE_FP4:
210 # If the user rejects FlashInfer remove those backends.
vllm/model_executor/layers/fused_moe/oracle/unquantized.py
+54 -10
@@ -9,6 +9,7 @@ from torch.nn import Module
9 import vllm.envs as envs
10 import vllm.model_executor.layers.fused_moe.modular_kernel as mk
11 from vllm._aiter_ops import rocm_aiter_ops
12 +from vllm.config.kernel import MoEBackend
13 from vllm.logger import init_logger
14 from vllm.model_executor.layers.fused_moe.config import (
15 FusedMoEConfig,
@@ -51,6 +52,22 @@ UNSUPPORTED_BACKEND = [
52 ]
53
54
55 +def map_unquantized_backend(runner_backend: MoEBackend) -> UnquantizedMoeBackend:
56 + """Map user's MoEBackend to UnquantizedMoeBackend."""
57 + mapping = {
58 + "triton": UnquantizedMoeBackend.TRITON,
59 + "flashinfer_trtllm": UnquantizedMoeBackend.FLASHINFER_TRTLLM,
60 + "flashinfer_cutlass": UnquantizedMoeBackend.FLASHINFER_CUTLASS,
61 + "aiter": UnquantizedMoeBackend.AITER,
62 + }
63 + if backend := mapping.get(runner_backend):
64 + return backend
65 + raise ValueError(
66 + f"moe_backend='{runner_backend}' is not supported for unquantized MoE. "
67 + f"Expected one of {list(mapping.keys())}."
68 + )
69 +
70 +
71 def select_unquantized_moe_backend(
72 moe_config: FusedMoEConfig,
73 use_ep: bool,
@@ -64,8 +81,6 @@ def select_unquantized_moe_backend(
81 def _make_log_backend(backend: UnquantizedMoeBackend):
82 return f"Using {backend.value} backend for Unquantized MoE"
83
67 - rocm_aiter_moe_enabled = rocm_aiter_ops.is_fused_moe_enabled()
68 -
84 activation_format = (
85 mk.FusedMoEActivationFormat.BatchedExperts
86 if moe_config.moe_parallel_config.use_batched_activation_format
@@ -77,20 +92,49 @@ def select_unquantized_moe_backend(
92 moe_config=moe_config,
93 activation_format=activation_format,
94 )
80 - flashinfer_trtllm_moe_enabled = (
81 - has_flashinfer()
82 - and envs.VLLM_USE_FLASHINFER_MOE_FP16
83 - and trtllm_supported
84 - and envs.VLLM_FLASHINFER_MOE_BACKEND == "latency"
85 - )
95 + flashinfer_trtllm_available = has_flashinfer() and trtllm_supported
96 # FlashInfer CUTLASS MoE is only supported on Hopper and later GPUS
87 - flashinfer_cutlass_moe_enabled = (
97 + flashinfer_cutlass_available = (
98 has_flashinfer_cutlass_fused_moe()
89 - and envs.VLLM_USE_FLASHINFER_MOE_FP16
99 and use_ep
100 and (not use_dp)
101 and current_platform.has_device_capability(90)
102 )
103 + flashinfer_trtllm_moe_enabled = (
104 + flashinfer_trtllm_available
105 + and envs.VLLM_USE_FLASHINFER_MOE_FP16
106 + and envs.VLLM_FLASHINFER_MOE_BACKEND == "latency"
107 + )
108 + flashinfer_cutlass_moe_enabled = (
109 + flashinfer_cutlass_available and envs.VLLM_USE_FLASHINFER_MOE_FP16
110 + )
111 + rocm_aiter_moe_enabled = rocm_aiter_ops.is_fused_moe_enabled()
112 +
113 + # Handle explicit moe_backend from user.
114 + runner_backend = moe_config.moe_backend
115 + if runner_backend != "auto":
116 + requested_backend = map_unquantized_backend(runner_backend)
117 + if requested_backend == UnquantizedMoeBackend.FLASHINFER_TRTLLM:
118 + if not flashinfer_trtllm_available:
119 + raise ValueError(
120 + "FlashInfer TRTLLM MoE backend is not available for this "
121 + "configuration."
122 + )
123 + elif requested_backend == UnquantizedMoeBackend.FLASHINFER_CUTLASS:
124 + if not flashinfer_cutlass_available:
125 + raise ValueError(
126 + "FlashInfer CUTLASS MoE backend is not available for this "
127 + "configuration."
128 + )
129 + elif requested_backend == UnquantizedMoeBackend.AITER and not (
130 + current_platform.is_rocm() and rocm_aiter_moe_enabled
131 + ):
132 + raise ValueError(
133 + "ROCm AITer MoE backend is not available for this configuration."
134 + )
135 + logger.info_once(_make_log_backend(requested_backend), scope="local")
136 + return requested_backend
137 +
138 if current_platform.is_rocm():
139 if rocm_aiter_moe_enabled:
140 backend = UnquantizedMoeBackend.AITER