Skip to content
20 changes: 15 additions & 5 deletions tests/kernels/moe/test_flashinfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,16 @@ class TestData:

@staticmethod
def make_moe_tensors_8bit(
m: int, k: int, n: int, e: int, reorder: bool
m: int, k: int, n: int, e: int, reorder: bool, activation: str = "silu"
) -> "TestData":
is_gated = True
if activation != "relu2_no_mul":
is_gated = False

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem right as it breaks test_flashinfer_per_tensor_moe_fp8_no_graph on blackwell
https://buildkite.com/vllm/ci/builds/38920/steps/canvas?jid=019a7fad-270b-4d40-8820-e3a1e75dc35e#019a7fad-270b-4d40-8820-e3a1e75dc35e/102-2387

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it should be if activation == "relu2_no_mul: , I originally wrote it as a one liner but the pre-commit hook complained, and I fixed it incorrectly. Changed it back and this should be correct.


hidden_states = torch.randn((m, k), device="cuda", dtype=torch.bfloat16) / 10
w13 = torch.randn((e, 2 * n, k), device="cuda", dtype=torch.bfloat16)
w13 = torch.randn(
(e, (2 * n) if is_gated else n, k), device="cuda", dtype=torch.bfloat16
)
w2 = torch.randn((e, k, n), device="cuda", dtype=torch.bfloat16)

# Scale to fp8
Expand Down Expand Up @@ -192,18 +198,22 @@ def test_flashinfer_per_tensor_moe_fp8_no_graph(
@pytest.mark.parametrize("m,n,k", MNK_FACTORS)
@pytest.mark.parametrize("e", NUM_EXPERTS)
@pytest.mark.parametrize("topk", TOP_KS)
@pytest.mark.parametrize("activation", ["silu", "relu2_no_mul"])
def test_flashinfer_cutlass_moe_fp8_no_graph(
m: int,
n: int,
k: int,
e: int,
topk: int,
activation: str,
monkeypatch,
):
current_platform.seed_everything(7)
monkeypatch.setenv("VLLM_FUSED_MOE_CHUNK_SIZE", "8192")
with set_current_vllm_config(vllm_config):
td = TestData.make_moe_tensors_8bit(m, k, n, e, reorder=False)
td = TestData.make_moe_tensors_8bit(
m, k, n, e, reorder=False, activation=activation
)

score = torch.randn((m, e), device="cuda", dtype=torch.bfloat16)
topk_weights, topk_ids, _ = FusedMoE.select_experts(
Expand Down Expand Up @@ -235,7 +245,7 @@ def test_flashinfer_cutlass_moe_fp8_no_graph(
topk_weights=topk_weights,
topk_ids=topk_ids,
inplace=False,
activation="silu",
activation=activation,
global_num_experts=e,
expert_map=None,
apply_router_weight_on_input=True,
Expand All @@ -255,7 +265,7 @@ def get_fused_moe_quant_config(n: torch.nn.Module) -> FusedMoEQuantConfig:
td.layer,
topk_weights,
topk_ids,
activation="silu",
activation=activation,
global_num_experts=e,
expert_map=None,
apply_router_weight_on_input=True,
Expand Down
11 changes: 9 additions & 2 deletions vllm/model_executor/layers/fused_moe/flashinfer_cutlass_moe.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,14 @@ def apply(
expert_tokens_meta: mk.ExpertTokensMetadata | None,
apply_router_weight_on_input: bool | None,
):
assert activation == "silu", (
Comment thread
amirkl94 marked this conversation as resolved.
"Only activation silu is supported in FlashInferExperts"
from flashinfer.fused_moe.core import ActivationType

activation_str_to_value_map = {
"silu": ActivationType.Swiglu, # This is the default
"relu2_no_mul": ActivationType.Relu2,
}
assert activation in activation_str_to_value_map, (
f"{activation=} missing from {activation_str_to_value_map.keys()=}"
)

if self.quant_dtype == torch.float8_e4m3fn:
Expand Down Expand Up @@ -196,6 +202,7 @@ def apply(
ep_size=self.ep_size,
ep_rank=self.ep_rank,
output=output,
activation_type=activation_str_to_value_map[activation],
)


Expand Down
33 changes: 20 additions & 13 deletions vllm/model_executor/layers/quantization/modelopt.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,12 +354,18 @@ def __init__(

self.cutlass_fp8_supported = cutlass_fp8_supported()
self.flashinfer_moe_backend: FlashinferMoeBackend | None = None
if (
envs.VLLM_USE_FLASHINFER_MOE_FP8
and has_flashinfer_moe()
and self.moe.is_act_and_mul
):
if envs.VLLM_USE_FLASHINFER_MOE_FP8 and has_flashinfer_moe():
self.flashinfer_moe_backend = get_flashinfer_moe_backend()
if (
self.flashinfer_moe_backend == FlashinferMoeBackend.TENSORRT_LLM
and not self.moe.is_act_and_mul
):
logger.info_once(
"Non-gated MoE is not supported for min-latency mode,"
"falling back to high-throughput mode"
)
Comment on lines +359 to +366

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems you are missing the override of self.flashinfer_moe_backend here

Comment on lines +359 to +366

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the self.flashinfer_moe_backend override was left out

self.flashinfer_moe_backend = FlashinferMoeBackend.CUTLASS

logger.info_once(
Comment on lines 355 to 369

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid enabling TensorRT flashinfer for relu2 activations

Removing the self.moe.is_act_and_mul guard means a flashinfer backend is now enabled whenever VLLM_USE_FLASHINFER_MOE_FP8 is set, regardless of the activation. If the user selects the latency backend (TensorRT‑LLM) and runs a relu2_no_mul model, apply() will hit the hard assertion activation == "silu" and abort instead of falling back to the existing non‑flashinfer path, which previously worked (albeit slower). Consider only enabling flashinfer when either the model is gated or the chosen backend is CUTLASS; otherwise leave flashinfer_moe_backend as None so non‑gated models continue to run.

Useful? React with 👍 / 👎.

f"Using FlashInfer {self.flashinfer_moe_backend.value} kernels"
)
Expand Down Expand Up @@ -557,10 +563,11 @@ def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
)

if self.flashinfer_moe_backend is not None:
layer.w13_weight.data = swap_w13_to_w31(layer.w13_weight.data)
register_moe_scaling_factors(layer)
if self.moe.is_act_and_mul:
layer.w13_weight.data = swap_w13_to_w31(layer.w13_weight.data)
if self.flashinfer_moe_backend == FlashinferMoeBackend.TENSORRT_LLM:
rotate_flashinfer_fp8_moe_weights(layer.w13_weight, layer.w2_weight)
register_moe_scaling_factors(layer)

def get_fused_moe_quant_config(
self, layer: torch.nn.Module
Expand All @@ -570,13 +577,13 @@ def get_fused_moe_quant_config(

return fp8_w8a8_moe_quant_config(
w1_scale=layer.w13_weight_scale,
g1_alphas=(layer.w13_weight_scale * layer.w13_input_scale).squeeze(),
g1_alphas=layer.output1_scales_gate_scalar.squeeze(),
w2_scale=layer.w2_weight_scale,
g2_alphas=(layer.w2_weight_scale * layer.w2_input_scale).squeeze(),
g2_alphas=layer.output2_scales_scalar.squeeze(),
a1_scale=layer.w13_input_scale,
a1_gscale=layer.w13_input_scale,
a2_scale=layer.w2_input_scale,
a2_gscale=1.0 / layer.w2_input_scale,
a2_gscale=layer.w2_input_scale_inv,
per_act_token_quant=False,
)

Expand Down Expand Up @@ -642,9 +649,9 @@ def apply(
)

if self.flashinfer_moe_backend == FlashinferMoeBackend.CUTLASS:
assert not renormalize
Comment thread
amirkl94 marked this conversation as resolved.
assert activation == "silu", (
f"Expected 'silu' activation but got {activation}"
assert activation in ("silu", "relu2_no_mul"), (
"Expected activation to be in ('silu', 'relu2_no_mul'),"
f"but got {activation}"
)
return flashinfer_cutlass_moe_fp8(
x,
Expand Down