[AMD] Enable cudagraph for aiter nsa backend and add aiter impl for nsa pr… (#18526)
This commit is contained in:
@@ -134,3 +134,63 @@ def act_quant(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return y, s
|
return y, s
|
||||||
|
|
||||||
|
|
||||||
|
@triton.jit
|
||||||
|
def _get_valid_kv_indices_kernel(
|
||||||
|
page_table_ptr, # [bs, topk]
|
||||||
|
kv_indptr_ptr, # [bs + 1]
|
||||||
|
kv_indices_ptr, # [bs * topk] output buffer
|
||||||
|
bs: tl.constexpr,
|
||||||
|
topk: tl.constexpr,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Extract valid indices (non -1) from page_table into kv_indices.
|
||||||
|
Each program handles one batch.
|
||||||
|
"""
|
||||||
|
batch_id = tl.program_id(0)
|
||||||
|
|
||||||
|
# Get the start position for this batch in kv_indices
|
||||||
|
dst_start = tl.load(kv_indptr_ptr + batch_id)
|
||||||
|
|
||||||
|
# Load all topk indices for this batch
|
||||||
|
src_offset = batch_id * topk
|
||||||
|
offsets = tl.arange(0, topk)
|
||||||
|
indices = tl.load(page_table_ptr + src_offset + offsets)
|
||||||
|
|
||||||
|
# Count valid indices and compact them
|
||||||
|
mask = indices != -1
|
||||||
|
|
||||||
|
# Use prefix sum to compute destination positions for valid elements
|
||||||
|
# For each position, count how many valid elements are before it
|
||||||
|
prefix_sum = tl.cumsum(mask.to(tl.int32), axis=0) - 1
|
||||||
|
|
||||||
|
# Store valid indices to their compacted positions
|
||||||
|
dst_positions = dst_start + prefix_sum
|
||||||
|
tl.store(kv_indices_ptr + dst_positions, indices, mask=mask)
|
||||||
|
|
||||||
|
|
||||||
|
def get_valid_kv_indices(
|
||||||
|
page_table_1: torch.Tensor,
|
||||||
|
kv_indptr: torch.Tensor,
|
||||||
|
kv_indices: torch.Tensor,
|
||||||
|
bs: int,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Extract valid indices from page_table_1 into kv_indices buffer.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
page_table_1: [bs, topk] page table with -1 as invalid
|
||||||
|
kv_indptr: [bs + 1] cumulative count of valid indices per batch
|
||||||
|
kv_indices: [bs * topk] pre-allocated output buffer
|
||||||
|
bs: batch size
|
||||||
|
"""
|
||||||
|
topk = page_table_1.shape[1]
|
||||||
|
grid = (bs,)
|
||||||
|
_get_valid_kv_indices_kernel[grid](
|
||||||
|
page_table_1,
|
||||||
|
kv_indptr,
|
||||||
|
kv_indices,
|
||||||
|
bs,
|
||||||
|
topk,
|
||||||
|
)
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ if TYPE_CHECKING:
|
|||||||
_is_hip = is_hip()
|
_is_hip = is_hip()
|
||||||
|
|
||||||
if _is_hip:
|
if _is_hip:
|
||||||
|
from sglang.srt.layers.attention.nsa.triton_kernel import get_valid_kv_indices
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from aiter import ( # noqa: F401
|
from aiter import ( # noqa: F401
|
||||||
flash_attn_varlen_func,
|
flash_attn_varlen_func,
|
||||||
@@ -333,6 +335,12 @@ class NativeSparseAttnBackend(
|
|||||||
(max_bs + 1,), dtype=torch.int32, device=model_runner.device
|
(max_bs + 1,), dtype=torch.int32, device=model_runner.device
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.kv_indices = torch.zeros(
|
||||||
|
max_bs * self.nsa_index_topk,
|
||||||
|
dtype=torch.int32,
|
||||||
|
device=self.device,
|
||||||
|
)
|
||||||
|
|
||||||
# Speculative decoding
|
# Speculative decoding
|
||||||
self.topk = model_runner.server_args.speculative_eagle_topk or 0
|
self.topk = model_runner.server_args.speculative_eagle_topk or 0
|
||||||
self.speculative_num_steps = speculative_num_steps
|
self.speculative_num_steps = speculative_num_steps
|
||||||
@@ -1476,6 +1484,15 @@ class NativeSparseAttnBackend(
|
|||||||
logit_cap=layer.logit_cap,
|
logit_cap=layer.logit_cap,
|
||||||
page_size=1,
|
page_size=1,
|
||||||
)
|
)
|
||||||
|
elif nsa_impl == "aiter":
|
||||||
|
if q_rope is not None:
|
||||||
|
q_all = torch.cat([q_nope, q_rope], dim=-1)
|
||||||
|
return self._forward_aiter_extend(
|
||||||
|
q_all=q_all,
|
||||||
|
kv_cache=kv_cache,
|
||||||
|
page_table_1=page_table_1,
|
||||||
|
layer=layer,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Unsupported {nsa_impl = } for forward_extend. Consider using an other attention backend."
|
f"Unsupported {nsa_impl = } for forward_extend. Consider using an other attention backend."
|
||||||
@@ -1861,7 +1878,8 @@ class NativeSparseAttnBackend(
|
|||||||
non_minus1_counts = non_minus1_mask.sum(dim=1)
|
non_minus1_counts = non_minus1_mask.sum(dim=1)
|
||||||
kv_indptr[1 : bs + 1] = torch.cumsum(non_minus1_counts, dim=0)
|
kv_indptr[1 : bs + 1] = torch.cumsum(non_minus1_counts, dim=0)
|
||||||
|
|
||||||
kv_indices = page_table_1[page_table_1 != -1]
|
kv_indices = self.kv_indices
|
||||||
|
get_valid_kv_indices(page_table_1, kv_indptr, kv_indices, bs)
|
||||||
|
|
||||||
mla_decode_fwd(
|
mla_decode_fwd(
|
||||||
q.view(-1, layer.tp_q_head_num, layer.head_dim),
|
q.view(-1, layer.tp_q_head_num, layer.head_dim),
|
||||||
@@ -1872,12 +1890,61 @@ class NativeSparseAttnBackend(
|
|||||||
kv_indices,
|
kv_indices,
|
||||||
metadata.cu_seqlens_q,
|
metadata.cu_seqlens_q,
|
||||||
metadata.max_seq_len_q,
|
metadata.max_seq_len_q,
|
||||||
layer.scaling,
|
sm_scale=layer.scaling,
|
||||||
layer.logit_cap,
|
logit_cap=layer.logit_cap,
|
||||||
)
|
)
|
||||||
# kv_cache = kv_cache.view(-1, 1, layer.head_dim)
|
# kv_cache = kv_cache.view(-1, 1, layer.head_dim)
|
||||||
return o
|
return o
|
||||||
|
|
||||||
|
def _forward_aiter_extend(
|
||||||
|
self,
|
||||||
|
q_all: torch.Tensor,
|
||||||
|
kv_cache: torch.Tensor,
|
||||||
|
page_table_1: torch.Tensor,
|
||||||
|
layer: RadixAttention,
|
||||||
|
) -> torch.Tensor:
|
||||||
|
num_tokens = q_all.shape[0]
|
||||||
|
q = q_all.reshape(-1, layer.tp_q_head_num * layer.head_dim)
|
||||||
|
|
||||||
|
if layer.head_dim != layer.v_head_dim:
|
||||||
|
o = q.new_empty((num_tokens, layer.tp_q_head_num * layer.v_head_dim))
|
||||||
|
else:
|
||||||
|
o = torch.empty_like(q)
|
||||||
|
|
||||||
|
non_minus1_mask = page_table_1 != -1
|
||||||
|
non_minus1_counts = non_minus1_mask.sum(dim=1)
|
||||||
|
|
||||||
|
kv_indptr = torch.zeros(num_tokens + 1, dtype=torch.int32, device=self.device)
|
||||||
|
kv_indptr[1:] = torch.cumsum(non_minus1_counts, dim=0)
|
||||||
|
|
||||||
|
# Allocate kv_indices with upper-bound size (num_tokens * topk)
|
||||||
|
topk = page_table_1.shape[1]
|
||||||
|
kv_indices = torch.zeros(
|
||||||
|
num_tokens * topk, dtype=torch.int32, device=self.device
|
||||||
|
)
|
||||||
|
|
||||||
|
# Use get_valid_kv_indices kernel to extract valid indices
|
||||||
|
get_valid_kv_indices(page_table_1, kv_indptr, kv_indices, num_tokens)
|
||||||
|
|
||||||
|
# Build cu_seqlens_q for extend: each token is treated as seq_len_q=1
|
||||||
|
cu_seqlens_q = torch.arange(
|
||||||
|
0, num_tokens + 1, dtype=torch.int32, device=self.device
|
||||||
|
)
|
||||||
|
# TODO support more forward_mode
|
||||||
|
mla_decode_fwd(
|
||||||
|
q.view(-1, layer.tp_q_head_num, layer.head_dim),
|
||||||
|
kv_cache.view(-1, 1, 1, layer.head_dim),
|
||||||
|
o.view(-1, layer.tp_q_head_num, layer.v_head_dim),
|
||||||
|
cu_seqlens_q,
|
||||||
|
kv_indptr,
|
||||||
|
kv_indices,
|
||||||
|
cu_seqlens_q,
|
||||||
|
1, # max_seq_len_q = 1 for per-token attention
|
||||||
|
sm_scale=layer.scaling,
|
||||||
|
logit_cap=layer.logit_cap,
|
||||||
|
)
|
||||||
|
return o
|
||||||
|
|
||||||
def _forward_trtllm(
|
def _forward_trtllm(
|
||||||
self,
|
self,
|
||||||
q: torch.Tensor,
|
q: torch.Tensor,
|
||||||
|
|||||||
Reference in New Issue
Block a user