Skip to content

Commit c3c6276

Browse files
committed
support ascend with vllm 0.20.2
1 parent d1653d9 commit c3c6276

11 files changed

Lines changed: 1297 additions & 117 deletions

File tree

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

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,83 @@ def attention_backend(self, use_mla: bool = False, use_sparse: bool = False) ->
146146
raise NotImplementedError("MLA with sparse attention is not implemented for Ascend yet.")
147147
return "vllm_fl.dispatch.backends.vendor.ascend.impl.attention.AscendMLABackend"
148148
return "vllm_fl.dispatch.backends.vendor.ascend.impl.attention.AscendAttentionBackend"
149+
150+
def invoke_fused_moe_triton_kernel(
151+
self,
152+
A,
153+
B,
154+
C,
155+
A_scale,
156+
B_scale,
157+
topk_weights,
158+
sorted_token_ids,
159+
expert_ids,
160+
num_tokens_post_padded,
161+
mul_routed_weight,
162+
top_k,
163+
config,
164+
compute_type=None,
165+
use_fp8_w8a8=False,
166+
use_int8_w8a8=False,
167+
use_int8_w8a16=False,
168+
use_int4_w4a16=False,
169+
per_channel_quant=False,
170+
block_shape=None,
171+
B_bias=None,
172+
):
173+
"""Ascend NPU fused MoE kernel using torch.mm.
174+
175+
Replaces the FlagGems Triton kernel which overflows the NPU's
176+
unified buffer on certain model shapes.
177+
"""
178+
from .impl.fused_moe_kernel import invoke_fused_moe_torch
179+
invoke_fused_moe_torch(
180+
A, B, C, A_scale, B_scale, topk_weights,
181+
sorted_token_ids, expert_ids, num_tokens_post_padded,
182+
mul_routed_weight, top_k, config,
183+
use_fp8_w8a8=use_fp8_w8a8,
184+
use_int8_w8a8=use_int8_w8a8,
185+
B_bias=B_bias,
186+
)
187+
188+
def moe_align_block_size(
189+
self,
190+
topk_ids,
191+
block_size,
192+
num_experts,
193+
expert_map=None,
194+
pad_sorted_ids=False,
195+
ignore_invalid_experts=False,
196+
):
197+
"""Pure-torch moe_align_block_size for Ascend NPU.
198+
199+
Replaces the FlagGems Triton kernel which causes DDR address OOB
200+
errors on Ascend NPU hardware.
201+
"""
202+
from .impl.fused_moe_kernel import moe_align_block_size_torch
203+
return moe_align_block_size_torch(
204+
topk_ids, block_size, num_experts, expert_map,
205+
pad_sorted_ids, ignore_invalid_experts,
206+
)
207+
208+
def moe_sum(self, inp, out):
209+
"""Pure-torch moe_sum: sum over top_k dimension."""
210+
# inp is (M, top_k, N), out is (M, N)
211+
# Avoid out= parameter which can cause NPU issues
212+
result = inp.sum(dim=1)
213+
out.copy_(result)
214+
215+
def topk_softmax(
216+
self, topk_weights, topk_indices, token_expert_indices, gating_output,
217+
renormalize=False,
218+
):
219+
"""Pure-torch topk_softmax for Ascend NPU."""
220+
scores = torch.softmax(gating_output.float(), dim=-1)
221+
topk = topk_weights.shape[1]
222+
tk_weights, tk_indices = torch.topk(scores, k=topk, dim=-1)
223+
topk_weights.copy_(tk_weights.to(topk_weights.dtype))
224+
topk_indices.copy_(tk_indices.to(topk_indices.dtype))
225+
if renormalize:
226+
s = topk_weights.sum(dim=-1, keepdim=True)
227+
topk_weights.div_(s.clamp(min=1e-8))
228+
return topk_weights, topk_indices

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

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from vllm.config import VllmConfig, get_current_vllm_config
3838
from vllm.utils.math_utils import cdiv
3939
from vllm.v1.attention.backend import AttentionCGSupport
40+
from vllm.v1.attention.backends.registry import AttentionBackendEnum, register_backend
4041
from vllm.v1.attention.backends.utils import CommonAttentionMetadata
4142

4243
from vllm_fl.dispatch.backends.vendor.ascend.impl.attention_mask import (
@@ -215,6 +216,7 @@ class AscendAttentionMetadataBuilder:
215216
# ACL graph support - ALWAYS means full graph capture is supported
216217
aclgraph_support: ClassVar[AttentionCGSupport] = AttentionCGSupport.ALWAYS
217218
reorder_batch_threshold: ClassVar[int] = 1
219+
supports_update_block_table: bool = False
218220

219221
@staticmethod
220222
def get_cudagraph_support(vllm_config, kv_cache_spec) -> AttentionCGSupport:
@@ -433,7 +435,7 @@ class AscendAttentionBackend(AttentionBackend):
433435

434436
@staticmethod
435437
def get_name() -> str:
436-
return "ASCEND_FL"
438+
return "CUSTOM"
437439

438440
@staticmethod
439441
def get_impl_cls() -> Type["AscendAttentionBackendImpl"]:
@@ -443,6 +445,15 @@ def get_impl_cls() -> Type["AscendAttentionBackendImpl"]:
443445
def get_builder_cls() -> Type["AscendAttentionMetadataBuilder"]:
444446
return AscendAttentionMetadataBuilder
445447

448+
@staticmethod
449+
def get_supported_kernel_block_sizes() -> list[int]:
450+
# Ascend fused_infer_attention_score and paged_attention kernels
451+
# are validated for block size 128 in vllm-ascend. Allowing the
452+
# default MultipleOf(1) lets the V1 engine pick unsupported merged
453+
# storage block sizes (e.g. 784 for Qwen3.5 hybrid models), which
454+
# causes aclnnFusedInferAttentionScoreV3 to fail with error 561002.
455+
return [128]
456+
446457
@staticmethod
447458
def get_kv_cache_shape(
448459
num_blocks: int,
@@ -488,6 +499,12 @@ def get_supported_block_size() -> list[int]:
488499
return [128]
489500

490501

502+
register_backend(
503+
AttentionBackendEnum.CUSTOM,
504+
"vllm_fl.dispatch.backends.vendor.ascend.impl.attention.AscendAttentionBackend",
505+
)
506+
507+
491508
class AscendAttentionBackendImpl(AttentionImpl):
492509
"""
493510
Ascend attention implementation using native torch_npu operators.
@@ -561,20 +578,16 @@ def _get_fia_params(
561578
value = self.value_cache.view(num_block, block_size, -1)
562579
actual_seq_lengths_kv = attn_metadata.seq_lens_list
563580
elif attn_metadata.attn_state == AscendAttentionState.DecodeOnly:
564-
# num_block, block_size, _, _ = self.key_cache.shape
565-
# key = self.key_cache.view(num_block, block_size, -1)
566-
# value = self.value_cache.view(num_block, block_size, -1)
567-
key = self.key_cache.view(-1, block_size, 256)
568-
value = self.value_cache.view(-1, block_size, 256)
581+
num_block, block_size, _, _ = self.key_cache.shape
582+
key = self.key_cache.view(num_block, block_size, -1)
583+
value = self.value_cache.view(num_block, block_size, -1)
569584
block_table = attn_metadata.block_tables
570585
actual_seq_lengths_kv = attn_metadata.seq_lens_list
571586
else:
572587
# ChunkedPrefill
573-
# num_block, block_size, _, _ = self.key_cache.shape
574-
# key = self.key_cache.view(num_block, block_size, -1)
575-
# value = self.value_cache.view(num_block, block_size, -1)
576-
key = self.key_cache.view(-1, block_size, 256)
577-
value = self.value_cache.view(-1, block_size, 256)
588+
num_block, block_size, _, _ = self.key_cache.shape
589+
key = self.key_cache.view(num_block, block_size, -1)
590+
value = self.value_cache.view(num_block, block_size, -1)
578591
block_table = attn_metadata.block_tables
579592
actual_seq_lengths_kv = attn_metadata.seq_lens_list
580593

@@ -596,7 +609,6 @@ def reshape_and_cache(
596609
# TODO(yxa): block_table.py: CUDA uses int64, NPU uses int32.
597610
if slots.dtype != torch.int32:
598611
slots = slots.to(torch.int32)
599-
# Use torch_npu reshape_and_cache
600612
torch_npu._npu_reshape_and_cache(
601613
key=key[:attn_metadata.num_actual_tokens],
602614
value=value[:attn_metadata.num_actual_tokens],
@@ -625,9 +637,9 @@ def forward_fused_infer_attention(
625637
key = key[:num_tokens]
626638
value = value[:num_tokens]
627639

628-
# Determine sparse_mode based on mask availability
629-
# sparse_mode=3 requires attn_mask; sparse_mode=0 does not
630-
# sparse_mode = 3 if attn_metadata.attn_mask is not None else 0
640+
# sparse_mode: 3 = causal with mask, 0 = no mask
641+
sparse_mode = 3 if attn_metadata.attn_mask is not None else 0
642+
631643
attn_output, _ = torch_npu.npu_fused_infer_attention_score(
632644
query=query,
633645
key=key,
@@ -641,7 +653,7 @@ def forward_fused_infer_attention(
641653
num_key_value_heads=self.num_kv_heads,
642654
num_heads=self.num_heads,
643655
scale=self.scale,
644-
sparse_mode=3,
656+
sparse_mode=sparse_mode,
645657
)
646658

647659
attn_output = attn_output.view(num_tokens, self.num_heads, self.head_size)
@@ -779,8 +791,9 @@ def forward(
779791
return output.fill_(0)
780792

781793
# Reshape and cache KV
782-
if attn_metadata != AscendAttentionState.DecodeOnly:
783-
kv_cache = [i.contiguous() for i in kv_cache]
794+
# Note: kv_cache[0]/[1] may be non-contiguous views of a
795+
# [2, num_blocks, ...] tensor. _npu_reshape_and_cache handles
796+
# them directly via slot_indices — no contiguous copy needed.
784797
if key is not None and value is not None:
785798
key = key.contiguous()
786799
value = value.contiguous()

0 commit comments

Comments
 (0)