Skip to content

Commit 82047df

Browse files
committed
Minimize code diff in ascend patches
1 parent 011774c commit 82047df

2 files changed

Lines changed: 336 additions & 36 deletions

File tree

vllm_fl/dispatch/backends/vendor/ascend/impl/fused_moe.py

Lines changed: 136 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,118 @@
1111
import torch_npu
1212
from flag_gems.runtime.backend._ascend import fused
1313

14+
import logging
15+
logger = logging.getLogger(__name__)
16+
17+
18+
def _npu_grouped_matmul_fused_experts(
19+
hidden_states: torch.Tensor,
20+
w1: torch.Tensor,
21+
w2: torch.Tensor,
22+
topk_weights: torch.Tensor,
23+
topk_ids: torch.Tensor,
24+
inplace: bool = False,
25+
activation: str = "silu",
26+
apply_router_weight_on_input: bool = False,
27+
global_num_experts: int = -1,
28+
expert_map: torch.Tensor | None = None,
29+
) -> torch.Tensor:
30+
"""Optimized MoE using npu_grouped_matmul — single batched kernel for all experts.
31+
32+
Replaces the Python for-loop over experts with:
33+
1. npu_moe_init_routing_v2 — sort tokens by expert, get per-expert counts
34+
2. npu_grouped_matmul — batched gate_up projection (all experts in one call)
35+
3. npu_swiglu — fused SiLU+mul activation
36+
4. npu_grouped_matmul — batched down projection
37+
5. npu_moe_token_unpermute — scatter results back with router weights
38+
"""
39+
num_tokens, hidden_dim = hidden_states.shape
40+
E, N, _ = w1.shape # w1: [E, N, K_in]
41+
top_k = topk_ids.shape[1]
42+
43+
if global_num_experts == -1:
44+
global_num_experts = E
45+
46+
# Handle expert_map for tensor parallel
47+
if expert_map is not None:
48+
local_topk_ids = expert_map[topk_ids.long()]
49+
# Mask invalid experts (mapped to -1)
50+
valid_mask = local_topk_ids >= 0
51+
topk_weights = topk_weights * valid_mask.to(topk_weights.dtype)
52+
topk_ids_for_routing = local_topk_ids.to(torch.int32)
53+
else:
54+
topk_ids_for_routing = topk_ids.to(torch.int32)
55+
56+
# Apply router weight on input if needed
57+
if apply_router_weight_on_input:
58+
# Scale hidden states by topk weights before routing
59+
# For this path, we need to expand hidden states first
60+
pass # Handled below in the unpermute step
61+
62+
# Step 1: Sort tokens by expert using npu_moe_init_routing_v2
63+
sorted_hidden_states, expanded_row_idx, expert_tokens, _ = (
64+
torch_npu.npu_moe_init_routing_v2(
65+
hidden_states,
66+
topk_ids_for_routing,
67+
active_num=num_tokens * top_k,
68+
expert_num=E,
69+
expert_tokens_num_type=1, # count mode
70+
expert_tokens_num_flag=True,
71+
active_expert_range=[0, E],
72+
quant_mode=-1, # no quantization
73+
)
74+
)
75+
expert_tokens = expert_tokens.to(torch.int64)
76+
77+
# Step 2: Gate-up projection — npu_grouped_matmul
78+
# w1 is [E, N, K] — grouped_matmul expects weight as [E, K, N] with split_item=2
79+
# split_item=2 means the weight K dimension splits across the group_list
80+
gate_up_out = torch_npu.npu_grouped_matmul(
81+
x=[sorted_hidden_states],
82+
weight=[w1.transpose(1, 2).contiguous()],
83+
split_item=2,
84+
group_list_type=1,
85+
group_type=0,
86+
group_list=expert_tokens,
87+
)[0]
88+
89+
# Step 3: Activation
90+
if activation == "silu":
91+
gate_up_out = torch_npu.npu_swiglu(gate_up_out)
92+
elif activation == "gelu":
93+
gate_up_out = torch_npu.npu_gelu_mul(gate_up_out)
94+
elif activation == "silu_no_mul":
95+
gate_up_out = F.silu(gate_up_out)
96+
elif activation == "gelu_no_mul":
97+
gate_up_out = torch_npu.npu_gelu(gate_up_out)
98+
else:
99+
raise ValueError(f"Unsupported FusedMoe activation: {activation}.")
100+
101+
# Step 4: Down projection — npu_grouped_matmul
102+
# w2 is [E, K_out, N//2] — need transpose to [E, N//2, K_out]
103+
down_out = torch_npu.npu_grouped_matmul(
104+
x=[gate_up_out],
105+
weight=[w2.transpose(1, 2).contiguous()],
106+
split_item=2,
107+
group_list_type=1,
108+
group_type=0,
109+
group_list=expert_tokens,
110+
)[0]
111+
112+
# Step 5: Unpermute and apply router weights
113+
# npu_moe_token_unpermute expects sorted_indices as int32
114+
expanded_row_idx_abs = torch.abs(expanded_row_idx).to(torch.int32)
115+
out = torch_npu.npu_moe_token_unpermute(
116+
permuted_tokens=down_out,
117+
sorted_indices=expanded_row_idx_abs,
118+
probs=topk_weights.to(down_out.dtype) if not apply_router_weight_on_input else None,
119+
)
120+
121+
if inplace:
122+
hidden_states.copy_(out)
123+
return hidden_states
124+
return out
125+
14126

15127
def _torch_fused_experts_impl(
16128
hidden_states: torch.Tensor,
@@ -138,8 +250,30 @@ def fused_experts_impl(
138250
assert w2.stride(-1) == 1, "Stride of last dimension must be 1"
139251
assert hidden_states.dtype in [torch.float32, torch.float16, torch.bfloat16]
140252

141-
# Use pure-torch implementation on NPU to avoid Triton kernel
142-
# compatibility issues with the Ascend backend.
253+
# Try optimized npu_grouped_matmul path first
254+
try:
255+
return _npu_grouped_matmul_fused_experts(
256+
hidden_states=hidden_states,
257+
w1=w1,
258+
w2=w2,
259+
topk_weights=topk_weights,
260+
topk_ids=topk_ids,
261+
inplace=inplace,
262+
activation=activation,
263+
apply_router_weight_on_input=apply_router_weight_on_input,
264+
global_num_experts=global_num_experts,
265+
expert_map=expert_map,
266+
)
267+
except Exception as e:
268+
# Fall back to Python loop on first failure, then log warning
269+
if not hasattr(fused_experts_impl, '_grouped_matmul_warned'):
270+
logger.warning(
271+
"npu_grouped_matmul MoE failed (%s), falling back to torch.mm loop. "
272+
"This warning will not repeat.", e
273+
)
274+
fused_experts_impl._grouped_matmul_warned = True
275+
276+
# Fallback: pure-torch implementation
143277
return _torch_fused_experts_impl(
144278
hidden_states=hidden_states,
145279
w1=w1,

0 commit comments

Comments
 (0)