Enable Flashinfer TRTLLM-GEN-MoE FP8 blockwise kernel for Qwen3-Next on Blackwell (#12543)
This commit is contained in:
@@ -68,6 +68,7 @@ class DeepEPMoE(FusedMoE):
|
|||||||
prefix: str = "",
|
prefix: str = "",
|
||||||
activation: str = "silu",
|
activation: str = "silu",
|
||||||
routed_scaling_factor: Optional[float] = None,
|
routed_scaling_factor: Optional[float] = None,
|
||||||
|
**kwargs,
|
||||||
):
|
):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
num_experts=num_experts,
|
num_experts=num_experts,
|
||||||
@@ -81,6 +82,7 @@ class DeepEPMoE(FusedMoE):
|
|||||||
prefix=prefix,
|
prefix=prefix,
|
||||||
activation=activation,
|
activation=activation,
|
||||||
routed_scaling_factor=routed_scaling_factor,
|
routed_scaling_factor=routed_scaling_factor,
|
||||||
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
if _use_aiter or _is_npu:
|
if _use_aiter or _is_npu:
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ from sglang.srt.layers.moe.token_dispatcher.standard import (
|
|||||||
StandardDispatchOutput,
|
StandardDispatchOutput,
|
||||||
)
|
)
|
||||||
from sglang.srt.layers.moe.topk import TopKOutput, TopKOutputChecker
|
from sglang.srt.layers.moe.topk import TopKOutput, TopKOutputChecker
|
||||||
|
from sglang.srt.layers.moe.utils import RoutingMethodType
|
||||||
from sglang.srt.layers.quantization.base_config import (
|
from sglang.srt.layers.quantization.base_config import (
|
||||||
FusedMoEMethodBase,
|
FusedMoEMethodBase,
|
||||||
QuantizationConfig,
|
QuantizationConfig,
|
||||||
@@ -56,7 +57,7 @@ from sglang.srt.utils import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if is_flashinfer_available():
|
if is_flashinfer_available():
|
||||||
from flashinfer import RoutingMethodType, fp4_quantize
|
from flashinfer import fp4_quantize
|
||||||
|
|
||||||
# Try to import FP4 TRTLLM function if flashinfer is available
|
# Try to import FP4 TRTLLM function if flashinfer is available
|
||||||
trtllm_fp4_block_scale_moe = None
|
trtllm_fp4_block_scale_moe = None
|
||||||
@@ -145,6 +146,7 @@ class FusedMoE(torch.nn.Module):
|
|||||||
gemm1_clamp_limit: Optional[float] = None,
|
gemm1_clamp_limit: Optional[float] = None,
|
||||||
use_weight_loader_fused: bool = False,
|
use_weight_loader_fused: bool = False,
|
||||||
with_bias=False,
|
with_bias=False,
|
||||||
|
routing_method_type: Optional[RoutingMethodType] = None,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
if params_dtype is None:
|
if params_dtype is None:
|
||||||
@@ -249,6 +251,8 @@ class FusedMoE(torch.nn.Module):
|
|||||||
and get_moe_runner_backend().is_cutlass()
|
and get_moe_runner_backend().is_cutlass()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.routing_method_type = routing_method_type
|
||||||
|
|
||||||
def _load_per_tensor_weight_scale(
|
def _load_per_tensor_weight_scale(
|
||||||
self,
|
self,
|
||||||
shard_id: str,
|
shard_id: str,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from enum import Enum
|
from enum import Enum, IntEnum
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
@@ -248,3 +248,22 @@ def speculative_moe_backend_context():
|
|||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
MOE_RUNNER_BACKEND = original_backend
|
MOE_RUNNER_BACKEND = original_backend
|
||||||
|
|
||||||
|
|
||||||
|
# The type of method in top-K routing, for use in torch custom op
|
||||||
|
# Please keep this in sync with the counterpart defined in https://github.com/flashinfer-ai/flashinfer/blob/main/include/flashinfer/trtllm/fused_moe/runner.h
|
||||||
|
class RoutingMethodType(IntEnum):
|
||||||
|
# Default: Softmax -> TopK
|
||||||
|
Default = (0,)
|
||||||
|
# Renormalize: TopK -> Softmax
|
||||||
|
Renormalize = (1,)
|
||||||
|
# DeepSeekV3: Sigmoid -> RoutingBiasAdd -> Top2 in group -> Top4 groups -> Top8 experts from the Top4 groups
|
||||||
|
DeepSeekV3 = (2,)
|
||||||
|
# Llama4: Top1 -> Sigmoid
|
||||||
|
Llama4 = (3,)
|
||||||
|
# Qwen3: Softmax -> TopK -> Renormalize
|
||||||
|
RenormalizeNaive = (4,)
|
||||||
|
# TopK only (no softmax)
|
||||||
|
TopK = (5,)
|
||||||
|
# Unspecified
|
||||||
|
Unspecified = 6
|
||||||
|
|||||||
@@ -1203,6 +1203,7 @@ class Fp8MoEMethod(FusedMoEMethodBase):
|
|||||||
from flashinfer.fused_moe import trtllm_fp8_block_scale_moe
|
from flashinfer.fused_moe import trtllm_fp8_block_scale_moe
|
||||||
|
|
||||||
from sglang.srt.layers.moe.topk import TopKOutputChecker
|
from sglang.srt.layers.moe.topk import TopKOutputChecker
|
||||||
|
from sglang.srt.layers.moe.utils import RoutingMethodType
|
||||||
|
|
||||||
assert TopKOutputChecker.format_is_bypassed(topk_output)
|
assert TopKOutputChecker.format_is_bypassed(topk_output)
|
||||||
router_logits = topk_output.router_logits
|
router_logits = topk_output.router_logits
|
||||||
@@ -1214,26 +1215,30 @@ class Fp8MoEMethod(FusedMoEMethodBase):
|
|||||||
# NOTE: scales of hidden states have to be transposed!
|
# NOTE: scales of hidden states have to be transposed!
|
||||||
a_sf_t = a_sf.t().contiguous()
|
a_sf_t = a_sf.t().contiguous()
|
||||||
|
|
||||||
assert (
|
|
||||||
topk_config.num_expert_group is not None
|
|
||||||
and topk_config.topk_group is not None
|
|
||||||
), "Current trtllm_fp8_block_scale_moe kernel does not support these two arguments as None"
|
|
||||||
|
|
||||||
correction_bias = (
|
correction_bias = (
|
||||||
None
|
None
|
||||||
if topk_config.correction_bias is None
|
if topk_config.correction_bias is None
|
||||||
else topk_config.correction_bias.to(x.dtype)
|
else topk_config.correction_bias.to(x.dtype)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
routing_method_type = getattr(
|
||||||
|
layer, "routing_method_type", RoutingMethodType.DeepSeekV3
|
||||||
|
)
|
||||||
|
|
||||||
with use_symmetric_memory(
|
with use_symmetric_memory(
|
||||||
get_tp_group(), disabled=not is_allocation_symmetric()
|
get_tp_group(), disabled=not is_allocation_symmetric()
|
||||||
):
|
):
|
||||||
|
|
||||||
# FIXME: there is a bug in the trtllm_fp8_block_scale_moe.
|
# FIXME: there is a bug in the trtllm_fp8_block_scale_moe.
|
||||||
# It ignored the `output`` argument. https://github.com/flashinfer-ai/flashinfer/blob/da01b1bd8f9f22aec8c0eea189ad54860b034947/flashinfer/fused_moe/core.py#L1323-L1325
|
# It ignored the `output`` argument. https://github.com/flashinfer-ai/flashinfer/blob/da01b1bd8f9f22aec8c0eea189ad54860b034947/flashinfer/fused_moe/core.py#L1323-L1325
|
||||||
# so we put the whole function under the ``use_symmetric_memory`` context manager.
|
# so we put the whole function under the ``use_symmetric_memory`` context manager.
|
||||||
# If the bug is fixed, we can only put the output tensor allocation under the context manager.
|
# If the bug is fixed, we can only put the output tensor allocation under the context manager.
|
||||||
return trtllm_fp8_block_scale_moe(
|
return trtllm_fp8_block_scale_moe(
|
||||||
routing_logits=router_logits.to(torch.float32),
|
routing_logits=(
|
||||||
|
router_logits.to(torch.float32)
|
||||||
|
if routing_method_type == RoutingMethodType.DeepSeekV3
|
||||||
|
else router_logits
|
||||||
|
),
|
||||||
routing_bias=correction_bias,
|
routing_bias=correction_bias,
|
||||||
hidden_states=a_q,
|
hidden_states=a_q,
|
||||||
hidden_states_scale=a_sf_t,
|
hidden_states_scale=a_sf_t,
|
||||||
@@ -1254,7 +1259,7 @@ class Fp8MoEMethod(FusedMoEMethodBase):
|
|||||||
tile_tokens_dim=get_tile_tokens_dim(
|
tile_tokens_dim=get_tile_tokens_dim(
|
||||||
x.shape[0], topk_config.top_k, layer.num_experts
|
x.shape[0], topk_config.top_k, layer.num_experts
|
||||||
),
|
),
|
||||||
routing_method_type=2, # DeepSeek-styled routing method
|
routing_method_type=routing_method_type,
|
||||||
use_shuffled_weight=False,
|
use_shuffled_weight=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ from sglang.srt.layers.moe import get_moe_a2a_backend
|
|||||||
from sglang.srt.layers.moe.ep_moe.layer import get_moe_impl_class
|
from sglang.srt.layers.moe.ep_moe.layer import get_moe_impl_class
|
||||||
from sglang.srt.layers.moe.fused_moe_triton import FusedMoE
|
from sglang.srt.layers.moe.fused_moe_triton import FusedMoE
|
||||||
from sglang.srt.layers.moe.topk import TopK
|
from sglang.srt.layers.moe.topk import TopK
|
||||||
|
from sglang.srt.layers.moe.utils import RoutingMethodType
|
||||||
from sglang.srt.layers.quantization.base_config import QuantizationConfig
|
from sglang.srt.layers.quantization.base_config import QuantizationConfig
|
||||||
from sglang.srt.layers.radix_attention import RadixAttention
|
from sglang.srt.layers.radix_attention import RadixAttention
|
||||||
from sglang.srt.layers.rotary_embedding import get_rope
|
from sglang.srt.layers.rotary_embedding import get_rope
|
||||||
@@ -162,6 +163,7 @@ class Qwen2MoeSparseMoeBlock(nn.Module):
|
|||||||
intermediate_size=config.moe_intermediate_size,
|
intermediate_size=config.moe_intermediate_size,
|
||||||
quant_config=quant_config,
|
quant_config=quant_config,
|
||||||
prefix=add_prefix("experts", prefix),
|
prefix=add_prefix("experts", prefix),
|
||||||
|
routing_method_type=RoutingMethodType.RenormalizeNaive,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.gate = ReplicatedLinear(
|
self.gate = ReplicatedLinear(
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
from sglang.srt.utils import kill_process_tree
|
||||||
|
from sglang.test.few_shot_gsm8k import run_eval
|
||||||
|
from sglang.test.test_utils import (
|
||||||
|
DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
|
||||||
|
DEFAULT_URL_FOR_TEST,
|
||||||
|
CustomTestCase,
|
||||||
|
popen_launch_server,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestFlashinferTrtllmGenMoeBackend(CustomTestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.model = "Qwen/Qwen3-Next-80B-A3B-Instruct-FP8"
|
||||||
|
cls.base_url = DEFAULT_URL_FOR_TEST
|
||||||
|
cls.process = popen_launch_server(
|
||||||
|
cls.model,
|
||||||
|
cls.base_url,
|
||||||
|
timeout=DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
|
||||||
|
env={**os.environ, "SGLANG_ENABLE_JIT_DEEPGEMM": "False"},
|
||||||
|
other_args=[
|
||||||
|
"--attention-backend",
|
||||||
|
"triton",
|
||||||
|
"--moe-runner-backend",
|
||||||
|
"flashinfer_trtllm",
|
||||||
|
"--cuda-graph-max-bs",
|
||||||
|
"512",
|
||||||
|
"--tp-size",
|
||||||
|
"4",
|
||||||
|
"--ep-size",
|
||||||
|
"4",
|
||||||
|
"--mem-fraction-static",
|
||||||
|
"0.7",
|
||||||
|
"--mamba-ssm-dtype",
|
||||||
|
"bfloat16",
|
||||||
|
"--quantization",
|
||||||
|
"fp8",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
kill_process_tree(cls.process.pid)
|
||||||
|
|
||||||
|
def test_gsm8k(self):
|
||||||
|
args = SimpleNamespace(
|
||||||
|
num_shots=5,
|
||||||
|
data_path=None,
|
||||||
|
num_questions=200,
|
||||||
|
max_new_tokens=512,
|
||||||
|
parallel=128,
|
||||||
|
host="http://127.0.0.1",
|
||||||
|
port=int(self.base_url.split(":")[-1]),
|
||||||
|
)
|
||||||
|
metrics = run_eval(args)
|
||||||
|
print(f"{metrics=}")
|
||||||
|
self.assertGreater(metrics["accuracy"], 0.93)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -217,6 +217,7 @@ suites = {
|
|||||||
TestFile("test_cpp_radix_cache.py", 60),
|
TestFile("test_cpp_radix_cache.py", 60),
|
||||||
],
|
],
|
||||||
"nightly-4-gpu-b200": [
|
"nightly-4-gpu-b200": [
|
||||||
|
TestFile("nightly/test_flashinfer_trtllm_gen_moe_backend.py", 300),
|
||||||
TestFile("test_fp4_moe.py", 300),
|
TestFile("test_fp4_moe.py", 300),
|
||||||
TestFile("nightly/test_gpt_oss_4gpu_perf.py", 600),
|
TestFile("nightly/test_gpt_oss_4gpu_perf.py", 600),
|
||||||
TestFile("nightly/test_flashinfer_trtllm_gen_attn_backend.py", 300),
|
TestFile("nightly/test_flashinfer_trtllm_gen_attn_backend.py", 300),
|
||||||
|
|||||||
Reference in New Issue
Block a user