Files
project_6/ex_engine/python/corex_moe.py
EX Engine 1ae398eeee fix(interface): corex_moe accepts w13 merged format + no silent fallback
corex_moe.py: moe_forward now accepts both formats:
  Format A: w1(E,I,H) + w2(E,H,I) + w3(E,I,H) — xllm style, separate gate/up
  Format B: w13(E,2*I,H) + w2(E,H,I) + w3=None — vllm style, merged gate_up
  Auto-detects by checking if w3 is None, splits w13 internally.

qwen3_5.py:
  - Fix corex_moe call: use keyword args (w3=None, topk=self.top_k)
    prevents topk integer going to w3 tensor position
  - Remove silent fallback on corex_moe failure — raise RuntimeError
    with full shape info for diagnosis. Zero score with no error log
    is worse than a crash.
2026-08-10 04:36:16 +00:00

220 lines
7.6 KiB
Python

"""
corex_moe.py — Fused MoE dispatch for BI-V100
MoE topk_softmax: CUDA kernel (moe_topk_softmax_v3.cu)
- Verified on BI-V100: sum=1.0, no NaN, no duplicate ids, 881 batch OK
- Warp shuffle only, zero shared memory, 64 experts specialized
- Falls back ONLY if .so compilation fails at build time
MoE expert GEMM: torch.matmul → cublas (libcublas.so in base image)
MoE activation: ixformer.silu_and_mul (confirmed working in base image)
"""
import os
import logging
import torch
import torch.nn.functional as F
from typing import Optional, Tuple
logger = logging.getLogger(__name__)
# ---------------------------------------------------------------------------
# Load CUDA topk_softmax kernel
# ---------------------------------------------------------------------------
_topk_ext = None
_topk_cuda_available = False
def _load_topk_kernel():
"""Load or JIT-compile the moe_topk_softmax CUDA kernel."""
global _topk_ext, _topk_cuda_available
if _topk_cuda_available:
return True
# Try pre-compiled .so first
search_paths = [
"/root/.cache/torch_extensions/py310_cu102/moe_topk_softmax_v3/moe_topk_softmax_v3.so",
"/workspace/ex_engine/build/moe_topk_softmax_v3.so",
]
for so_path in search_paths:
if os.path.isfile(so_path):
try:
import importlib.util
spec = importlib.util.spec_from_file_location("moe_topk_softmax_v3", so_path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
_topk_ext = mod
_topk_cuda_available = True
logger.info(f"Loaded moe_topk_softmax CUDA kernel from {so_path}")
return True
except Exception as e:
logger.debug(f"Failed to load {so_path}: {e}")
# JIT compile from source
cu_search = [
"/workspace/ex_engine/csrc/moe_topk_softmax_v3.cu",
# Deployed by patch_ops.sh into vllm models dir
os.path.join(os.path.dirname(os.path.abspath(__file__)), "moe_topk_softmax_v3.cu"),
]
# Also search in vllm model_executor/models/
try:
import vllm
vllm_models = os.path.join(os.path.dirname(vllm.__file__), "model_executor", "models")
cu_search.append(os.path.join(vllm_models, "moe_topk_softmax_v3.cu"))
except Exception:
pass
for cu_path in cu_search:
if os.path.isfile(cu_path):
try:
from torch.utils.cpp_extension import load
_topk_ext = load(
name="moe_topk_softmax_v3",
sources=[cu_path],
extra_cuda_cflags=["-O3"],
verbose=False,
)
_topk_cuda_available = True
logger.info(f"JIT-compiled moe_topk_softmax from {cu_path}")
return True
except Exception as e:
logger.warning(f"JIT compile failed: {e}")
logger.error("moe_topk_softmax CUDA kernel not available — cannot proceed")
return False
# ---------------------------------------------------------------------------
# ixformer optional
# ---------------------------------------------------------------------------
_ix = None
try:
import ixformer as _ix
except ImportError:
pass
# ---------------------------------------------------------------------------
# topk_softmax — CUDA kernel (no fallback)
# ---------------------------------------------------------------------------
def topk_softmax(
gating_output: torch.Tensor,
topk: int,
renormalize: bool = True,
) -> Tuple[torch.Tensor, torch.Tensor]:
"""
Fused softmax + top-k via CUDA kernel.
Returns: (topk_weights [num_tokens, topk], topk_ids [num_tokens, topk])
"""
if not _topk_cuda_available:
_load_topk_kernel()
if _topk_cuda_available and _topk_ext is not None:
results = _topk_ext.moe_topk_softmax(gating_output, topk, renormalize)
return results[0], results[1] # weights, ids
# NO FALLBACK — raise error
raise RuntimeError(
"moe_topk_softmax CUDA kernel not available. "
"Build it first: python3 -c 'from torch.utils.cpp_extension import load; "
"load(name=\"moe_topk_softmax_v3\", "
"sources=[\"ex_engine/csrc/moe_topk_softmax_v3.cu\"], "
"extra_cuda_cflags=[\"-O3\"])'"
)
# ---------------------------------------------------------------------------
# MoE forward — full pipeline
# ---------------------------------------------------------------------------
def moe_forward(
hidden_states: torch.Tensor,
gate_output: torch.Tensor,
w1_or_w13: torch.Tensor,
w2: torch.Tensor,
w3: Optional[torch.Tensor] = None,
topk: int = 8,
renormalize: bool = True,
**kwargs,
) -> torch.Tensor:
"""
Full MoE pipeline: CUDA topk → per-expert GEMM (cublas) → silu → GEMM → scatter-add.
Accepts two weight formats:
Format A (xllm style): w1=(E,I,H), w2=(E,H,I), w3=(E,I,H) — gate and up separate
Format B (vllm style): w13=(E,2*I,H), w2=(E,H,I), w3=None — gate_up merged
"""
num_tokens = hidden_states.shape[0]
hidden_size = hidden_states.shape[1]
dtype = hidden_states.dtype
# Detect weight format
if w3 is None:
# Format B: w13 merged — split into w1 (gate) and w3 (up)
w13 = w1_or_w13
inter2 = w13.shape[1]
w1 = w13[:, :inter2 // 2, :] # (E, I, H)
w3 = w13[:, inter2 // 2:, :] # (E, I, H)
else:
w1 = w1_or_w13
topk_weights, topk_ids = topk_softmax(gate_output, topk, renormalize)
num_experts = w1.shape[0]
flat_ids = topk_ids.view(-1)
flat_weights = topk_weights.view(-1)
expanded_hidden = hidden_states.unsqueeze(1).expand(
-1, topk, -1
).reshape(-1, hidden_size)
output = torch.zeros_like(expanded_hidden)
for expert_idx in range(num_experts):
mask = (flat_ids == expert_idx)
if not mask.any():
continue
expert_tokens = expanded_hidden[mask]
gate_out = expert_tokens @ w1[expert_idx].t()
up_out = expert_tokens @ w3[expert_idx].t()
# SiLU activation
if _ix is not None:
fused_input = torch.cat([gate_out, up_out], dim=-1)
activated = torch.empty_like(gate_out)
try:
_ix.silu_and_mul(fused_input, activated)
except Exception:
activated = F.silu(gate_out) * up_out
else:
activated = F.silu(gate_out) * up_out
expert_out = activated @ w2[expert_idx].t()
output[mask] = expert_out
output = output * flat_weights.unsqueeze(-1).to(output.dtype)
output = output.view(num_tokens, topk, hidden_size).sum(dim=1)
return output
# ---------------------------------------------------------------------------
# Logging wrappers
# ---------------------------------------------------------------------------
_prefill_logged = False
_decode_logged = False
def moe_prefill(hidden_states, gate_output, w1, w2, w3, topk=8, renormalize=True, **kw):
global _prefill_logged
if not _prefill_logged:
logger.info(f"Using CoreX fused MoE prefill operator: "
f"tokens={hidden_states.shape[0]}, kernel=topk-warp-shuffle+cublas-gemm")
_prefill_logged = True
return moe_forward(hidden_states, gate_output, w1, w2, w3, topk, renormalize)
def moe_decode(hidden_states, gate_output, w1, w2, w3, topk=8, renormalize=True, **kw):
global _decode_logged
if not _decode_logged:
logger.info("Using CoreX fused MoE decode operator")
_decode_logged = True
return moe_forward(hidden_states, gate_output, w1, w2, w3, topk, renormalize)