Skip to content

Commit 78697d2

Browse files
ping1jing2Makcum888eVDV1985
authored andcommitted
Ascend attention backend(PA&MLA) (sgl-project#7722)
Co-authored-by: Maksim <makcum888e@mail.ru> Co-authored-by: VDV1985 <vladdv85@mail.ru>
1 parent 87f0d33 commit 78697d2

17 files changed

Lines changed: 842 additions & 16 deletions

File tree

docs/backend/attention_backend.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
| **Triton** ||||||
1010
| **Torch Native** ||||||
1111
| **FlashMLA** ||||||
12+
| **Ascend** ||||||
1213

1314
Note: Every kernel backend is compatible with a page size > 1 by specifying an argument such as `--page-size 16`.
1415
This is because a page size of 16 can be converted to a page size of 1 in the kernel backend.
@@ -46,3 +47,8 @@ python3 -m sglang.launch_server --model meta-llama/Meta-Llama-3.1-8B-Instruct --
4647
python3 -m sglang.launch_server --tp 8 --model deepseek-ai/DeepSeek-R1 --attention-backend flashmla --trust-remote-code
4748
python3 -m sglang.launch_server --tp 8 --model deepseek-ai/DeepSeek-R1 --attention-backend flashmla --kv-cache-dtype fp8_e4m3 --trust-remote-code
4849
```
50+
51+
- Ascend
52+
```bash
53+
python3 -m sglang.launch_server --model meta-llama/Meta-Llama-3.1-8B-Instruct --attention-backend ascend
54+
```
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
from __future__ import annotations
2+
3+
from dataclasses import dataclass
4+
from typing import TYPE_CHECKING, Optional
5+
6+
import torch
7+
import torch_npu
8+
from torch.nn.functional import scaled_dot_product_attention
9+
10+
from sglang.srt.configs.model_config import AttentionArch
11+
from sglang.srt.layers.attention.base_attn_backend import AttentionBackend
12+
from sglang.srt.layers.attention.torch_native_backend import TorchNativeAttnBackend
13+
from sglang.srt.layers.radix_attention import AttentionType
14+
from sglang.srt.model_executor.forward_batch_info import ForwardBatch
15+
16+
if TYPE_CHECKING:
17+
from sglang.srt.layers.radix_attention import RadixAttention
18+
from sglang.srt.model_executor.model_runner import ModelRunner
19+
20+
21+
@dataclass
22+
class ForwardMetadata:
23+
24+
# calculated map for kv positions [bs * maxseqlen]
25+
block_tables: Optional[torch.Tensor] = None
26+
27+
# seq len inputs
28+
extend_seq_lens_cpu_int: Optional[torch.Tensor] = None
29+
seq_lens_cpu_int: Optional[torch.Tensor] = None
30+
31+
32+
class AscendAttnBackend(AttentionBackend):
33+
34+
def gen_attention_mask(self, max_seq_len: int, dtype=torch.float16):
35+
mask_flag = torch.tril(
36+
torch.ones((max_seq_len, max_seq_len), dtype=torch.bool)
37+
).view(max_seq_len, max_seq_len)
38+
mask_flag = ~mask_flag
39+
if dtype == torch.float16:
40+
mask_value = torch.finfo(torch.float32).min
41+
else:
42+
mask_value = 1
43+
self.mask = (
44+
torch.masked_fill(
45+
torch.zeros(size=(max_seq_len, max_seq_len)), mask_flag, mask_value
46+
)
47+
.to(dtype)
48+
.to(self.device)
49+
)
50+
self.mask_len = max_seq_len
51+
52+
def __init__(self, model_runner: ModelRunner):
53+
super().__init__()
54+
self.forward_metadata = ForwardMetadata()
55+
self.device = model_runner.device
56+
self.gen_attention_mask(128, model_runner.dtype)
57+
self.page_size = model_runner.page_size
58+
self.use_mla = model_runner.model_config.attention_arch == AttentionArch.MLA
59+
if self.use_mla:
60+
self.kv_lora_rank = model_runner.model_config.kv_lora_rank
61+
self.qk_rope_head_dim = model_runner.model_config.qk_rope_head_dim
62+
self.native_attn = TorchNativeAttnBackend(model_runner)
63+
64+
def init_forward_metadata(self, forward_batch: ForwardBatch):
65+
"""Init the metadata for a forward pass."""
66+
self.forward_metadata.block_tables = (
67+
forward_batch.req_to_token_pool.req_to_token[
68+
forward_batch.req_pool_indices, : forward_batch.seq_lens.max()
69+
][:, :: self.page_size]
70+
// self.page_size
71+
)
72+
if forward_batch.extend_seq_lens is not None:
73+
self.forward_metadata.extend_seq_lens_cpu_int = (
74+
forward_batch.extend_seq_lens.cpu().int()
75+
)
76+
self.forward_metadata.seq_lens_cpu_int = forward_batch.seq_lens_cpu.int()
77+
78+
def forward_extend(
79+
self,
80+
q,
81+
k,
82+
v,
83+
layer: RadixAttention,
84+
forward_batch: ForwardBatch,
85+
save_kv_cache=True,
86+
):
87+
if save_kv_cache:
88+
forward_batch.token_to_kv_pool.set_kv_buffer(
89+
layer, forward_batch.out_cache_loc, k, v
90+
)
91+
92+
k_cache = forward_batch.token_to_kv_pool.get_key_buffer(layer.layer_id)
93+
v_cache = forward_batch.token_to_kv_pool.get_value_buffer(layer.layer_id)
94+
95+
if not self.use_mla:
96+
query = q.view(-1, layer.tp_q_head_num * layer.qk_head_dim)
97+
output = torch.empty(
98+
(query.shape[0], layer.tp_q_head_num * layer.v_head_dim),
99+
dtype=query.dtype,
100+
device=query.device,
101+
)
102+
103+
torch_npu._npu_flash_attention_qlens(
104+
query=query,
105+
key_cache=k_cache,
106+
value_cache=v_cache,
107+
mask=self.mask,
108+
block_table=self.forward_metadata.block_tables,
109+
seq_len=self.forward_metadata.extend_seq_lens_cpu_int,
110+
context_lens=self.forward_metadata.seq_lens_cpu_int,
111+
scale_value=layer.scaling,
112+
num_heads=layer.tp_q_head_num,
113+
num_kv_heads=layer.tp_k_head_num,
114+
out=output,
115+
)
116+
return output
117+
else:
118+
if layer.qk_head_dim != layer.v_head_dim:
119+
o = q.new_empty((q.shape[0], layer.tp_q_head_num * layer.v_head_dim))
120+
else:
121+
o = torch.empty_like(q)
122+
123+
use_gqa = layer.tp_q_head_num != layer.tp_k_head_num
124+
125+
q_ = q.view(-1, layer.tp_q_head_num, layer.qk_head_dim)
126+
o_ = o.view(-1, layer.tp_q_head_num, layer.v_head_dim)
127+
128+
causal = True
129+
if (
130+
layer.is_cross_attention
131+
or layer.attn_type == AttentionType.ENCODER_ONLY
132+
):
133+
causal = False
134+
135+
self.native_attn._run_sdpa_forward_extend(
136+
q_,
137+
o_,
138+
k_cache.view(
139+
-1, layer.tp_k_head_num, (self.kv_lora_rank + self.qk_rope_head_dim)
140+
),
141+
v_cache.view(-1, layer.tp_v_head_num, self.kv_lora_rank),
142+
forward_batch.req_to_token_pool.req_to_token,
143+
forward_batch.req_pool_indices,
144+
forward_batch.seq_lens,
145+
forward_batch.extend_prefix_lens,
146+
forward_batch.extend_seq_lens,
147+
scaling=layer.scaling,
148+
enable_gqa=use_gqa,
149+
causal=causal,
150+
)
151+
return o
152+
153+
def forward_decode(
154+
self,
155+
q: torch.Tensor,
156+
k: torch.Tensor,
157+
v: torch.Tensor,
158+
layer: RadixAttention,
159+
forward_batch: ForwardBatch,
160+
save_kv_cache=True,
161+
):
162+
if save_kv_cache:
163+
forward_batch.token_to_kv_pool.set_kv_buffer(
164+
layer, forward_batch.out_cache_loc, k, v
165+
)
166+
if not self.use_mla:
167+
k_cache = forward_batch.token_to_kv_pool.get_key_buffer(layer.layer_id)
168+
v_cache = forward_batch.token_to_kv_pool.get_value_buffer(layer.layer_id)
169+
170+
query = q.view(-1, layer.tp_q_head_num, layer.qk_head_dim)
171+
num_tokens = query.shape[0]
172+
output = torch.empty(
173+
(num_tokens, layer.tp_q_head_num, layer.v_head_dim),
174+
dtype=query.dtype,
175+
device=query.device,
176+
)
177+
178+
torch_npu._npu_paged_attention(
179+
query=query,
180+
key_cache=k_cache,
181+
value_cache=v_cache,
182+
num_heads=layer.tp_q_head_num,
183+
num_kv_heads=layer.tp_k_head_num,
184+
scale_value=layer.scaling,
185+
block_table=self.forward_metadata.block_tables,
186+
context_lens=self.forward_metadata.seq_lens_cpu_int,
187+
out=output,
188+
)
189+
return output.view(num_tokens, layer.tp_q_head_num * layer.v_head_dim)
190+
else:
191+
query = q.view(-1, layer.tp_q_head_num, layer.head_dim)
192+
num_tokens = query.shape[0]
193+
kv_c_and_k_pe_cache = forward_batch.token_to_kv_pool.get_key_buffer(
194+
layer.layer_id
195+
)
196+
kv_c_and_k_pe_cache = kv_c_and_k_pe_cache.view(
197+
-1,
198+
self.page_size,
199+
layer.tp_k_head_num,
200+
self.kv_lora_rank + self.qk_rope_head_dim,
201+
)
202+
203+
attn_output = torch.empty(
204+
[num_tokens, layer.tp_q_head_num, self.kv_lora_rank],
205+
dtype=q.dtype,
206+
device=q.device,
207+
)
208+
torch_npu._npu_paged_attention_mla(
209+
query=query,
210+
key_cache=kv_c_and_k_pe_cache,
211+
num_kv_heads=layer.tp_k_head_num,
212+
num_heads=layer.tp_q_head_num,
213+
scale_value=layer.scaling,
214+
block_table=self.forward_metadata.block_tables,
215+
context_lens=self.forward_metadata.seq_lens_cpu_int,
216+
mla_vheadsize=self.kv_lora_rank,
217+
out=attn_output,
218+
)
219+
return attn_output.view(num_tokens, layer.tp_q_head_num * self.kv_lora_rank)

python/sglang/srt/layers/moe/ep_moe/layer.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
import einops
55
import torch
6-
from sgl_kernel import silu_and_mul
76
from torch.nn import Module
87

98
from sglang.srt.custom_op import CustomOp
@@ -50,13 +49,18 @@
5049
dispose_tensor,
5150
get_bool_env_var,
5251
is_hip,
52+
is_npu,
5353
set_weight_attrs,
5454
)
5555

5656
_is_hip = is_hip()
57+
_is_npu = is_npu()
5758
_is_fp8_fnuz = is_fp8_fnuz()
5859
_use_aiter = get_bool_env_var("SGLANG_USE_AITER") and _is_hip
5960

61+
if not _is_npu:
62+
from sgl_kernel import silu_and_mul
63+
6064
if _is_hip:
6165
from vllm._custom_ops import scaled_fp8_quant
6266

python/sglang/srt/layers/moe/fused_moe_triton/layer.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,44 @@ def forward_cpu(
321321
routed_scaling_factor,
322322
)
323323

324+
def forward_npu(
325+
self,
326+
layer: torch.nn.Module,
327+
x: torch.Tensor,
328+
use_grouped_topk: bool,
329+
top_k: int,
330+
router_logits: torch.Tensor,
331+
renormalize: bool,
332+
topk_group: Optional[int] = None,
333+
num_expert_group: Optional[int] = None,
334+
num_fused_shared_experts: int = 0,
335+
custom_routing_function: Optional[Callable] = None,
336+
correction_bias: Optional[torch.Tensor] = None,
337+
activation: str = "silu",
338+
apply_router_weight_on_input: bool = False,
339+
inplace: bool = True,
340+
no_combine: bool = False,
341+
routed_scaling_factor: Optional[float] = None,
342+
) -> torch.Tensor:
343+
return moe_forward_native(
344+
layer,
345+
x,
346+
use_grouped_topk,
347+
top_k,
348+
router_logits,
349+
renormalize,
350+
topk_group,
351+
num_expert_group,
352+
num_fused_shared_experts,
353+
custom_routing_function,
354+
correction_bias,
355+
activation,
356+
apply_router_weight_on_input,
357+
inplace,
358+
no_combine,
359+
routed_scaling_factor,
360+
)
361+
324362
def forward_tpu(self, *args, **kwargs) -> torch.Tensor:
325363
raise NotImplementedError("The TPU backend currently does not support MoE.")
326364

python/sglang/srt/layers/moe/topk.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,15 @@
3535
is_cpu,
3636
is_cuda,
3737
is_hip,
38+
is_npu,
3839
)
3940

4041
_is_cuda = is_cuda()
4142
_is_hip = is_hip()
4243
_use_aiter = get_bool_env_var("SGLANG_USE_AITER") and _is_hip
4344
_is_cpu_amx_available = cpu_has_amx_support()
4445
_is_cpu = is_cpu()
46+
_is_npu = is_npu()
4547

4648
if _is_cuda:
4749
from sgl_kernel import moe_fused_gate
@@ -159,6 +161,9 @@ def grouped_topk_gpu(
159161
assert hidden_states.shape[0] == gating_output.shape[0], "Number of tokens mismatch"
160162

161163
scores = torch.softmax(gating_output, dim=-1)
164+
# NPU compiler limitation
165+
if _is_npu and scores.dtype == torch.bfloat16:
166+
scores = scores.to(torch.float16)
162167
num_token = scores.shape[0]
163168
num_experts = scores.shape[1]
164169
group_scores = (

python/sglang/srt/layers/rotary_embedding.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ def __init__(
660660
beta_slow: int = 1,
661661
mscale: float = 1,
662662
mscale_all_dim: float = 0,
663-
device: Optional[str] = "cuda",
663+
device: Optional[str] = "cuda" if not _is_npu else "npu",
664664
) -> None:
665665
self.scaling_factor = scaling_factor
666666
self.extrapolation_factor = extrapolation_factor
@@ -679,7 +679,7 @@ def __init__(
679679
)
680680

681681
# Re-dispatch
682-
if _is_hip:
682+
if _is_hip or _is_npu:
683683
self._forward_method = self.forward_native
684684

685685
def _compute_inv_freq(self, scaling_factor: float) -> torch.Tensor:

python/sglang/srt/managers/schedule_batch.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1673,6 +1673,7 @@ def get_model_worker_batch(
16731673
)
16741674
or global_server_args_dict["attention_backend"] == "flashmla"
16751675
or global_server_args_dict["attention_backend"] == "cutlass_mla"
1676+
or global_server_args_dict["attention_backend"] == "ascend"
16761677
or global_server_args_dict["enable_two_batch_overlap"]
16771678
):
16781679
seq_lens_cpu = (
@@ -1875,7 +1876,10 @@ def get_last_loc(
18751876
req_pool_indices_tensor: torch.Tensor,
18761877
prefix_lens_tensor: torch.Tensor,
18771878
) -> torch.Tensor:
1878-
if global_server_args_dict["attention_backend"] != "torch_native":
1879+
if (
1880+
global_server_args_dict["attention_backend"] != "ascend"
1881+
and global_server_args_dict["attention_backend"] != "torch_native"
1882+
):
18791883
impl = get_last_loc_triton
18801884
else:
18811885
impl = get_last_loc_torch

0 commit comments

Comments
 (0)