from __future__ import annotations import torch import triton import triton.language as tl from sglang.srt.utils import ( is_cpu, is_cuda, is_hip, is_musa, is_npu, is_xpu, next_power_of_2, ) _is_cpu = is_cpu() _is_cuda = is_cuda() _is_hip = is_hip() _is_npu = is_npu() _is_musa = is_musa() _is_xpu = is_xpu() if _is_cpu: from sgl_kernel import assign_extend_cache_locs_cpu, assign_req_to_token_pool_cpu @triton.jit def assign_req_to_token_pool( req_pool_indices, req_to_token, start_offset, end_offset, out_cache_loc, pool_len: tl.constexpr, bs_upper: tl.constexpr, ): BLOCK_SIZE: tl.constexpr = 32 pid = tl.program_id(axis=0) kv_start = tl.load(start_offset + pid) kv_end = tl.load(end_offset + pid) token_pool = req_to_token + tl.load(req_pool_indices + pid) * pool_len length_offset = tl.arange(0, bs_upper) start = tl.load(start_offset + length_offset, mask=length_offset < pid, other=0) end = tl.load(end_offset + length_offset, mask=length_offset < pid, other=0) out_offset = tl.sum(end - start, axis=0) out_cache_ptr = out_cache_loc + out_offset save_offset = tl.arange(0, BLOCK_SIZE) + kv_start load_offset = tl.arange(0, BLOCK_SIZE) num_loop = tl.cdiv(kv_end - kv_start, BLOCK_SIZE) for _ in range(num_loop): mask = save_offset < kv_end data = tl.load(out_cache_ptr + load_offset, mask=mask) tl.store(token_pool + save_offset, data, mask=mask) save_offset += BLOCK_SIZE load_offset += BLOCK_SIZE def assign_req_to_token_pool_func( req_pool_indices: torch.Tensor, req_to_token: torch.Tensor, start_offset: torch.Tensor, end_offset: torch.Tensor, out_cache_loc: torch.Tensor, batch_size: int, ): if _is_cpu: assign_req_to_token_pool_cpu( req_pool_indices, req_to_token, start_offset, end_offset, out_cache_loc, req_to_token.shape[1], ) return assign_req_to_token_pool[(batch_size,)]( req_pool_indices, req_to_token, start_offset, end_offset, out_cache_loc, req_to_token.shape[1], next_power_of_2(batch_size), ) @triton.jit def assign_draft_cache_locs_contiguous( req_pool_indices, req_to_token, seq_lens, out_cache_loc, pool_len: tl.constexpr, topk: tl.constexpr, speculative_num_steps: tl.constexpr, ): BLOCK_SIZE: tl.constexpr = 128 pid = tl.program_id(axis=0) copy_len = topk * speculative_num_steps out_cache_ptr = out_cache_loc + pid * topk * speculative_num_steps # Copy from req_to_token to out_cache_loc kv_start = tl.load(seq_lens + pid) token_pool = req_to_token + tl.load(req_pool_indices + pid) * pool_len num_loop = tl.cdiv(copy_len, BLOCK_SIZE) for i in range(num_loop): copy_offset = tl.arange(0, BLOCK_SIZE) + i * BLOCK_SIZE mask = copy_offset < copy_len data = tl.load(token_pool + kv_start + copy_offset, mask=mask) tl.store(out_cache_ptr + copy_offset, data, mask=mask) @triton.jit def generate_draft_decode_kv_indices( req_pool_indices, req_to_token, paged_kernel_lens, kv_indices, kv_indptr, positions, pool_len: tl.constexpr, kv_indices_stride: tl.constexpr, kv_indptr_stride: tl.constexpr, bs_upper: tl.constexpr, iter_upper: tl.constexpr, num_tokens_upper: tl.constexpr, page_size: tl.constexpr, ): BLOCK_SIZE: tl.constexpr = 128 iters = tl.program_id(axis=0) bid = tl.program_id(axis=1) topk_id = tl.program_id(axis=2) num_steps = tl.num_programs(axis=0) num_seqs = tl.num_programs(axis=1) topk = tl.num_programs(axis=2) kv_indices += kv_indices_stride * iters kv_indptr += kv_indptr_stride * iters iters += 1 load_offset = tl.arange(0, bs_upper) seq_lens = tl.load(paged_kernel_lens + load_offset, mask=load_offset < bid, other=0) seq_len = tl.load(paged_kernel_lens + bid) cum_seq_len = tl.sum(seq_lens) # Update kv_indices kv_offset = cum_seq_len * topk + bid * iters * topk + topk_id * (seq_len + iters) kv_ptr = kv_indices + kv_offset token_pool_ptr = req_to_token + tl.load(req_pool_indices + bid) * pool_len kv_offset = tl.arange(0, BLOCK_SIZE) num_loop = tl.cdiv(seq_len, BLOCK_SIZE) for _ in range(num_loop): mask = kv_offset < seq_len data = tl.load(token_pool_ptr + kv_offset, mask=mask) tl.store(kv_ptr + kv_offset, data, mask=mask) kv_offset += BLOCK_SIZE extend_offset = tl.arange(0, iter_upper) if page_size == 1 or topk == 1: extend_data = tl.load( token_pool_ptr + seq_len + topk_id * num_steps + tl.arange(0, iter_upper), mask=extend_offset < iters, ) else: prefix_len = seq_len last_page_len = prefix_len % page_size num_new_pages_per_topk = ( last_page_len + num_steps + page_size - 1 ) // page_size prefix_base = seq_len // page_size * page_size start = ( prefix_base + topk_id * num_new_pages_per_topk * page_size + last_page_len ) extend_data = tl.load( token_pool_ptr + start + extend_offset, mask=extend_offset < iters, ) tl.store(kv_ptr + seq_len + extend_offset, extend_data, mask=extend_offset < iters) # Update kv_indptr bs_offset = tl.arange(0, num_tokens_upper) zid = bid * topk + topk_id if zid == 0: zid = num_seqs * topk positions = tl.load(positions + bs_offset, mask=bs_offset < zid, other=0) base = tl.sum(positions) tl.store(kv_indptr + zid, base + zid * iters) @triton.jit def align_evict_mask_to_page_size( seq_lens, evict_mask, page_size: tl.constexpr, num_draft_tokens: tl.constexpr, BLOCK_SIZE: tl.constexpr, ): t_range = tl.arange(0, BLOCK_SIZE) bid = tl.program_id(axis=0) seq_len = tl.load(seq_lens + bid) io_mask = t_range < num_draft_tokens mask_row = tl.load( evict_mask + bid * num_draft_tokens + t_range, mask=io_mask, other=0 ) num_trues = tl.sum(mask_row) num_false = num_draft_tokens - num_trues start = (seq_len + num_false - 1) // page_size * page_size - seq_len for i in range(max(start, 0), min(start + page_size, num_draft_tokens)): tl.store(evict_mask + bid * num_draft_tokens + i, False) @torch.compile(dynamic=True, disable=_is_npu) def get_src_tgt_cache_loc( seq_lens: torch.Tensor, out_cache_loc: torch.Tensor, accept_index: torch.Tensor, num_correct_drafts: torch.Tensor, draft_token_num: int, page_size: int, ): src_cache_loc = out_cache_loc[accept_index] # zeros_like, not empty_like: any uncovered tail stays at slot 0 (padding) # instead of caching-allocator garbage. tgt_cache_loc = torch.zeros_like(src_cache_loc) extended_len = seq_lens + draft_token_num keep_len = torch.minimum( (seq_lens + num_correct_drafts + 1 + page_size - 1) // page_size * page_size, extended_len, ) to_free_num_slots = extended_len - keep_len return src_cache_loc, tgt_cache_loc, to_free_num_slots @triton.jit def get_target_cache_loc( tgt_cache_loc, to_free_slots, num_correct_drafts, to_free_num_slots, out_cache_loc, num_verify_tokens: tl.constexpr, num_verify_tokens_upper: tl.constexpr, bs_upper: tl.constexpr, ): bid = tl.program_id(axis=0) offset = tl.arange(0, num_verify_tokens_upper) bs_offset = tl.arange(0, bs_upper) # write the first part to tgt_cache_loc accept_len_all = tl.load(num_correct_drafts + bs_offset, mask=bs_offset < bid) tgt_cache_loc_start = tl.sum(accept_len_all) + bid copy_len = tl.load(num_correct_drafts + bid) + 1 out_cache_loc_row = tl.load( out_cache_loc + bid * num_verify_tokens + offset, mask=offset < copy_len ) tl.store( tgt_cache_loc + tgt_cache_loc_start + offset, out_cache_loc_row, mask=offset < copy_len, ) # write the second part to to_free_num_pages to_free_num_slots_all = tl.load(to_free_num_slots + bs_offset, mask=bs_offset < bid) to_free_num_slots_cur = tl.load(to_free_num_slots + bid) out_cache_loc_start = num_verify_tokens - to_free_num_slots_cur to_free_slots_start = tl.sum(to_free_num_slots_all) copy_len = to_free_num_slots_cur out_cache_loc_row = tl.load( out_cache_loc + bid * num_verify_tokens + out_cache_loc_start + offset, mask=offset < copy_len, ) tl.store( to_free_slots + to_free_slots_start + offset, out_cache_loc_row, mask=offset < copy_len, ) @triton.jit def filter_finished_cache_loc_kernel( out_cache_loc, tgt_cache_loc, num_correct_drafts, num_accept_tokens_filter, bs_upper: tl.constexpr, num_verify_tokens_upper: tl.constexpr, ): bid = tl.program_id(0) bs_offset = tl.arange(0, bs_upper) num_correct_drafts_all = tl.load( num_correct_drafts + bs_offset, mask=bs_offset < bid ) old_start = tl.sum(num_correct_drafts_all) + bid num_accept_tokens_filter_all = tl.load( num_accept_tokens_filter + bs_offset, mask=bs_offset < bid ) new_start = tl.sum(num_accept_tokens_filter_all) copy_len = tl.load(num_accept_tokens_filter + bid) copy_offset = tl.arange(0, num_verify_tokens_upper) value = tl.load( tgt_cache_loc + old_start + copy_offset, mask=copy_offset < copy_len ) tl.store( out_cache_loc + new_start + copy_offset, value, mask=copy_offset < copy_len ) @triton.jit def assign_extend_cache_locs( req_pool_indices, req_to_token, start_offset, end_offset, out_cache_loc, pool_len: tl.constexpr, bs_upper: tl.constexpr, ): BLOCK_SIZE: tl.constexpr = 32 pid = tl.program_id(axis=0) kv_start = tl.load(start_offset + pid) kv_end = tl.load(end_offset + pid) token_pool = req_to_token + tl.load(req_pool_indices + pid) * pool_len length_offset = tl.arange(0, bs_upper) start = tl.load(start_offset + length_offset, mask=length_offset < pid, other=0) end = tl.load(end_offset + length_offset, mask=length_offset < pid, other=0) out_offset = tl.sum(end - start, axis=0) out_cache_ptr = out_cache_loc + out_offset load_offset = tl.arange(0, BLOCK_SIZE) + kv_start save_offset = tl.arange(0, BLOCK_SIZE) num_loop = tl.cdiv(kv_end - kv_start, BLOCK_SIZE) for _ in range(num_loop): mask = load_offset < kv_end data = tl.load(token_pool + load_offset, mask=mask) tl.store(out_cache_ptr + save_offset, data, mask=mask) load_offset += BLOCK_SIZE save_offset += BLOCK_SIZE def assign_extend_cache_locs_func( req_pool_indices: torch.Tensor, req_to_token: torch.Tensor, start_offset: torch.Tensor, end_offset: torch.Tensor, batch_size: int, draft_token_num: int, device, ) -> torch.Tensor: if _is_cuda or _is_hip or _is_musa or _is_xpu: out_cache_loc = torch.empty( (batch_size * draft_token_num,), dtype=torch.int64, device=device, ) assign_extend_cache_locs[(batch_size,)]( req_pool_indices, req_to_token, start_offset, end_offset, out_cache_loc, req_to_token.shape[1], next_power_of_2(batch_size), ) return out_cache_loc elif _is_npu: out_cache_loc = torch.empty( (batch_size * draft_token_num,), dtype=torch.int32, device=device, ) torch.ops.npu.cache_loc_update( req_pool_indices, req_to_token, start_offset, end_offset, out_cache_loc, ) return out_cache_loc elif _is_cpu: out_cache_loc = torch.empty( (batch_size * draft_token_num,), dtype=torch.int64, device=device, ) assign_extend_cache_locs_cpu( req_pool_indices, req_to_token, start_offset, end_offset, out_cache_loc, req_to_token.shape[1], ) return out_cache_loc