| from abc import ABC |
|
|
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
|
|
| from diffulex.attention import fetch_attn_metadata |
| from diffulex.logger import get_logger |
| from diffulex.moe.config import ( |
| get_moe_intermediate_size, |
| get_num_experts, |
| get_num_experts_per_tok, |
| get_norm_topk_prob, |
| ) |
| from diffulex.moe.topk import build_topk_router |
| from diffulex_kernel import fused_moe |
| from diffulex.layer.activation import SiluAndMul |
| from diffulex.layer.linear import ColumnParallelLinear, RowParallelLinear |
|
|
| _VLLM_FUSED_MOE = None |
| _VLLM_FUSED_MOE_LOAD_ERR: Exception | None = None |
| _VLLM_MODULAR_LOAD_ERR: Exception | None = None |
| logger = get_logger(__name__) |
|
|
|
|
| def _load_vllm_fused_moe(): |
| """Load the vendored vLLM fused_moe implementation on demand. |
| |
| The vendored module imports vLLM, so keep this lazy to avoid making vLLM a |
| hard dependency unless the vLLM MoE backend is explicitly selected. |
| """ |
| global _VLLM_FUSED_MOE, _VLLM_FUSED_MOE_LOAD_ERR |
| if _VLLM_FUSED_MOE is not None: |
| return _VLLM_FUSED_MOE |
| if _VLLM_FUSED_MOE_LOAD_ERR is not None: |
| return None |
|
|
| try: |
| from diffulex_kernel.python.vllm_fuse_moe import fused_moe as vllm_fused_moe |
|
|
| _VLLM_FUSED_MOE = vllm_fused_moe |
| return _VLLM_FUSED_MOE |
| except Exception as exc: |
| _VLLM_FUSED_MOE_LOAD_ERR = exc |
| return None |
|
|
|
|
| def _load_vllm_modular_moe(): |
| global _VLLM_MODULAR_LOAD_ERR |
| if _VLLM_MODULAR_LOAD_ERR is not None: |
| return None |
|
|
| try: |
| from vllm.model_executor.layers.fused_moe.activation import MoEActivation |
| from vllm.model_executor.layers.fused_moe.config import ( |
| FUSED_MOE_UNQUANTIZED_CONFIG, |
| FusedMoEConfig, |
| FusedMoEParallelConfig, |
| RoutingMethodType, |
| ) |
| from vllm.model_executor.layers.fused_moe.fused_moe import TritonExperts |
| from vllm.model_executor.layers.fused_moe.modular_kernel import FusedMoEKernel |
| from vllm.model_executor.layers.fused_moe.prepare_finalize import ( |
| make_moe_prepare_and_finalize_no_dp_ep, |
| ) |
| from vllm.v1.worker.workspace import ( |
| init_workspace_manager, |
| is_workspace_manager_initialized, |
| ) |
|
|
| return { |
| "FUSED_MOE_UNQUANTIZED_CONFIG": FUSED_MOE_UNQUANTIZED_CONFIG, |
| "FusedMoEConfig": FusedMoEConfig, |
| "FusedMoEKernel": FusedMoEKernel, |
| "FusedMoEParallelConfig": FusedMoEParallelConfig, |
| "MoEActivation": MoEActivation, |
| "RoutingMethodType": RoutingMethodType, |
| "TritonExperts": TritonExperts, |
| "init_workspace_manager": init_workspace_manager, |
| "is_workspace_manager_initialized": is_workspace_manager_initialized, |
| "make_moe_prepare_and_finalize_no_dp_ep": make_moe_prepare_and_finalize_no_dp_ep, |
| } |
| except Exception as exc: |
| _VLLM_MODULAR_LOAD_ERR = exc |
| return None |
|
|
|
|
| class SharedExpertMLP(nn.Module): |
| def __init__(self, hidden_size: int, intermediate_size: int, *, hidden_act: str = "silu") -> None: |
| super().__init__() |
| if hidden_act != "silu": |
| raise NotImplementedError("SharedExpertMLP currently supports only silu.") |
| self.gate_proj = ColumnParallelLinear(hidden_size, intermediate_size, bias=False) |
| self.up_proj = ColumnParallelLinear(hidden_size, intermediate_size, bias=False) |
| self.down_proj = RowParallelLinear(intermediate_size, hidden_size, bias=False) |
| self.act_fn = SiluAndMul() |
|
|
| def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: |
| return self.down_proj( |
| self.act_fn(torch.cat((self.gate_proj(hidden_states), self.up_proj(hidden_states)), dim=-1)) |
| ) |
|
|
| class FusedMoE(nn.Module, ABC): |
|
|
| def __init__( |
| self, |
| hidden_size: int, |
| intermediate_size: int, |
| num_experts: int, |
| top_k: int, |
| *, |
| hidden_act: str = "silu", |
| norm_topk_prob: bool = True, |
| moe_gemm_impl: str = "triton", |
| num_shared_experts: int = 0, |
| shared_expert_intermediate_size: int | None = None, |
| ) -> None: |
| super().__init__() |
|
|
| if hidden_act != "silu": |
| raise NotImplementedError("only silu is supported currently") |
| |
| self.hidden_size = hidden_size |
| self.intermediate_size = intermediate_size |
| self.num_experts = num_experts |
| self.top_k = top_k |
| self.hidden_act = hidden_act |
| self.norm_topk_prob = norm_topk_prob |
| self.moe_gemm_impl = str(moe_gemm_impl) |
| self.num_shared_experts = num_shared_experts |
| self._vllm_modular_kernel = None |
| self._vllm_modular_logged = False |
|
|
| self.router = build_topk_router( |
| "triton", |
| top_k=top_k, |
| renormalize=norm_topk_prob, |
| scoring_func="softmax", |
| ) |
| self.shared_experts = None |
| if num_shared_experts > 0: |
| shared_intermediate_size = int(shared_expert_intermediate_size or intermediate_size * num_shared_experts) |
| self.shared_experts = SharedExpertMLP( |
| hidden_size, |
| shared_intermediate_size, |
| hidden_act=hidden_act, |
| ) |
|
|
| self.fetch_attn_metadata = fetch_attn_metadata |
|
|
| @classmethod |
| def from_config(cls, config) -> "FusedMoE": |
| return cls( |
| hidden_size=config.hidden_size, |
| intermediate_size=get_moe_intermediate_size(config), |
| num_experts=get_num_experts(config), |
| top_k=get_num_experts_per_tok(config), |
| hidden_act=getattr(config, "hidden_act", "silu"), |
| norm_topk_prob=get_norm_topk_prob(config), |
| moe_gemm_impl=getattr(config, "moe_gemm_impl", "triton"), |
| num_shared_experts=int(getattr(config, "num_shared_experts", 0) or 0), |
| ) |
| |
| def forward(self, hidden_states: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: |
| |
| raise NotImplementedError |
|
|
| def add_shared_experts(self, routed_states: torch.Tensor, hidden_states: torch.Tensor) -> torch.Tensor: |
| if self.shared_experts is None: |
| return routed_states |
| shared_states = self.shared_experts(hidden_states) |
| return routed_states + shared_states |
|
|
| @staticmethod |
| def _phase_from_prefill_flags(is_prefill) -> str: |
| if isinstance(is_prefill, bool): |
| return "prefill" if is_prefill else "decode" |
|
|
| if torch.is_tensor(is_prefill): |
| if is_prefill.numel() == 0: |
| return "unknown" |
| flags = is_prefill.to(dtype=torch.bool) |
| all_prefill = bool(flags.all().item()) |
| any_prefill = bool(flags.any().item()) |
| else: |
| try: |
| flags = [bool(flag) for flag in is_prefill] |
| except TypeError: |
| return "unknown" |
| if not flags: |
| return "unknown" |
| all_prefill = all(flags) |
| any_prefill = any(flags) |
|
|
| if all_prefill: |
| return "prefill" |
| if not any_prefill: |
| return "decode" |
| return "mixed" |
|
|
| def get_current_phase(self) -> str: |
| """Return the current inference phase for this forward pass.""" |
| try: |
| attn_metadata = self.fetch_attn_metadata() |
| except Exception: |
| return "unknown" |
|
|
| if attn_metadata is None: |
| return "unknown" |
|
|
| phase = self._phase_from_prefill_flags(attn_metadata.is_prefill) |
| if phase != "unknown": |
| return phase |
|
|
| status_table = attn_metadata.status_table |
| if status_table is None: |
| return "unknown" |
|
|
| if torch.is_tensor(status_table): |
| return self._phase_from_prefill_flags(status_table == 0) |
|
|
| try: |
| return self._phase_from_prefill_flags([int(status) == 0 for status in status_table]) |
| except TypeError: |
| return self._phase_from_prefill_flags(int(status_table) == 0) |
|
|
| def _build_vllm_modular_kernel(self, hidden_states: torch.Tensor, w13: torch.Tensor): |
| api = _load_vllm_modular_moe() |
| if api is None: |
| raise RuntimeError(f"vLLM modular MoE backend is unavailable: {_VLLM_MODULAR_LOAD_ERR!r}") |
| if str(self.hidden_act) != "silu": |
| raise NotImplementedError("vllm_modular MoE currently supports only silu activation.") |
|
|
| if not api["is_workspace_manager_initialized"](): |
| api["init_workspace_manager"](hidden_states.device) |
|
|
| parallel_config = api["FusedMoEParallelConfig"].make_no_parallel() |
| moe_config = api["FusedMoEConfig"]( |
| num_experts=int(w13.shape[0]), |
| experts_per_token=int(self.top_k), |
| hidden_dim=int(self.hidden_size), |
| intermediate_size_per_partition=int(self.intermediate_size), |
| num_local_experts=int(w13.shape[0]), |
| num_logical_experts=int(w13.shape[0]), |
| activation=api["MoEActivation"].SILU, |
| device=hidden_states.device, |
| routing_method=( |
| api["RoutingMethodType"].Renormalize |
| if self.norm_topk_prob |
| else api["RoutingMethodType"].Default |
| ), |
| moe_parallel_config=parallel_config, |
| in_dtype=hidden_states.dtype, |
| max_num_tokens=max(1, int(hidden_states.shape[0])), |
| ) |
| quant_config = api["FUSED_MOE_UNQUANTIZED_CONFIG"] |
| prepare_finalize = api["make_moe_prepare_and_finalize_no_dp_ep"](use_monolithic=False) |
| fused_experts = api["TritonExperts"](moe_config=moe_config, quant_config=quant_config) |
| if not self._vllm_modular_logged: |
| logger.info( |
| "Initialized vLLM modular MoE backend: hidden=%s intermediate=%s local_experts=%s " |
| "top_k=%s dtype=%s tokens=%s", |
| self.hidden_size, |
| self.intermediate_size, |
| int(w13.shape[0]), |
| self.top_k, |
| hidden_states.dtype, |
| int(hidden_states.shape[0]), |
| ) |
| self._vllm_modular_logged = True |
| return api["FusedMoEKernel"]( |
| prepare_finalize, |
| fused_experts, |
| shared_experts=None, |
| moe_parallel_config=parallel_config, |
| inplace=False, |
| ) |
|
|
| def _vllm_modular_expert_gemm( |
| self, |
| hidden_states: torch.Tensor, |
| w13: torch.Tensor, |
| w2: torch.Tensor, |
| topk_ids: torch.Tensor, |
| topk_weights: torch.Tensor, |
| local_expert_start: int, |
| ) -> torch.Tensor: |
| api = _load_vllm_modular_moe() |
| if api is None: |
| raise RuntimeError(f"vLLM modular MoE backend is unavailable: {_VLLM_MODULAR_LOAD_ERR!r}") |
| if self._vllm_modular_kernel is None: |
| self._vllm_modular_kernel = self._build_vllm_modular_kernel(hidden_states, w13) |
|
|
| local_topk_ids = topk_ids.to(torch.int64) - int(local_expert_start) |
| valid = (local_topk_ids >= 0) & (local_topk_ids < w13.shape[0]) |
| safe_topk_ids = torch.where(valid, local_topk_ids, torch.zeros_like(local_topk_ids)) |
| safe_topk_weights = torch.where(valid, topk_weights, torch.zeros_like(topk_weights)) |
| return self._vllm_modular_kernel.apply( |
| hidden_states=hidden_states, |
| w1=w13, |
| w2=w2, |
| topk_weights=safe_topk_weights, |
| topk_ids=safe_topk_ids, |
| activation=api["MoEActivation"].SILU, |
| global_num_experts=int(w13.shape[0]), |
| expert_map=None, |
| apply_router_weight_on_input=False, |
| shared_experts_input=None, |
| ) |
|
|
| @torch.compiler.disable |
| def expert_gemm( |
| self, |
| impl: str, |
| hidden_states: torch.Tensor, |
| w13: torch.Tensor, |
| w2: torch.Tensor, |
| topk_ids: torch.Tensor, |
| topk_weights: torch.Tensor, |
| local_expert_start: int = 0, |
| hidden_act: str = "silu" |
| ) -> torch.Tensor: |
| if impl == "triton": |
| out = fused_moe( |
| hidden_states=hidden_states, |
| w13=w13, |
| w2=w2, |
| topk_ids=topk_ids, |
| topk_weights=topk_weights, |
| local_expert_start=local_expert_start, |
| hidden_act=hidden_act, |
| ) |
| return out |
| if impl == "vllm_modular": |
| return self._vllm_modular_expert_gemm( |
| hidden_states=hidden_states, |
| w13=w13, |
| w2=w2, |
| topk_ids=topk_ids, |
| topk_weights=topk_weights, |
| local_expert_start=local_expert_start, |
| ) |
| if impl == "vllm": |
| vllm_fused_moe = _load_vllm_fused_moe() |
| if vllm_fused_moe is None: |
| |
| out = fused_moe( |
| hidden_states=hidden_states, |
| w13=w13, |
| w2=w2, |
| topk_ids=topk_ids, |
| topk_weights=topk_weights, |
| local_expert_start=local_expert_start, |
| hidden_act=hidden_act, |
| ) |
| else: |
| |
| local_topk_ids = topk_ids.to(torch.int64) - int(local_expert_start) |
| valid = (local_topk_ids >= 0) & (local_topk_ids < w13.shape[0]) |
| safe_topk_ids = torch.where(valid, local_topk_ids, torch.zeros_like(local_topk_ids)) |
| safe_topk_weights = torch.where(valid, topk_weights, torch.zeros_like(topk_weights)) |
| out = vllm_fused_moe( |
| hidden_states=hidden_states, |
| w1=w13, |
| w2=w2, |
| topk_weights=safe_topk_weights, |
| topk_ids=safe_topk_ids, |
| inplace=False, |
| ) |
| return out |
| if impl == "naive": |
| num_tokens, hidden_size = hidden_states.shape |
| num_local_experts = w13.shape[0] |
| intermediate_size = w13.shape[1] // 2 |
| final_hidden_states = hidden_states.new_zeros((num_tokens, hidden_size)) |
| local_topk_ids = topk_ids.to(torch.int64) - int(local_expert_start) |
| |
| for token_idx in range(num_tokens): |
| token_hidden = hidden_states[token_idx] |
| token_out = torch.zeros(hidden_size, device=hidden_states.device, dtype=torch.float32) |
|
|
| for slot_idx in range(topk_ids.shape[1]): |
| local_expert_idx = int(local_topk_ids[token_idx, slot_idx].item()) |
|
|
| if local_expert_idx < 0 or local_expert_idx >= num_local_experts: |
| continue |
|
|
| weight = topk_weights[token_idx, slot_idx] |
| if weight.item() == 0: |
| continue |
|
|
| expert_w13 = w13[local_expert_idx] |
| gate_proj = expert_w13[:intermediate_size] |
| up_proj = expert_w13[intermediate_size:] |
| |
| gate = torch.matmul(token_hidden, gate_proj.transpose(0, 1)) |
| up = torch.matmul(token_hidden, up_proj.transpose(0, 1)) |
| activated = F.silu(gate) * up |
| |
| expert_out = torch.matmul(activated, w2[local_expert_idx].transpose(0, 1)) |
| token_out += expert_out.float() * weight.float() |
| |
| final_hidden_states[token_idx] = token_out.to(hidden_states.dtype) |
|
|
| return final_hidden_states |
| raise ValueError(f"Unknown MoE expert_gemm impl: {impl}") |
|
|
|
|
| __all__ = ["FusedMoE", "SharedExpertMLP"] |
|
|