chore: import upstream snapshot with attribution
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:38:16 +08:00
commit 94057c3d3e
7152 changed files with 2120455 additions and 0 deletions
@@ -0,0 +1,14 @@
from __future__ import annotations
import torch
def inputs_on_cuda(*args, **kwargs) -> bool:
"""Route kernel dispatch by input placement: the first tensor argument
decides. CUDA inputs take the fused triton kernel; CPU inputs take the
torch reference implementation (triton is CUDA-only, and CPU-side callers
such as unit tests exercise the reference path)."""
for value in (*args, *kwargs.values()):
if isinstance(value, torch.Tensor):
return value.is_cuda
raise AssertionError("kernel dispatch requires at least one tensor argument")
@@ -0,0 +1,862 @@
from __future__ import annotations
from typing import Optional
import msgspec
import torch
import triton
import triton.language as tl
from sglang.srt.speculative.dflash_info_v2 import DFlashDraftInputV2
from sglang.srt.speculative.dflash_utils import (
_get_or_create_chain_verify_buffers,
build_dflash_verify_target_probs,
compute_dflash_correct_drafts_and_bonus,
)
from sglang.srt.speculative.dspark_components.kernels.dispatch import inputs_on_cuda
from sglang.srt.speculative.reject_sampling import chain_speculative_sampling_triton
class AcceptSampling:
@classmethod
def execute(
cls, *args, **kwargs
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
candidates: torch.Tensor,
target_logits: torch.Tensor,
draft_probs: torch.Tensor,
sampling_info,
draft_input: DFlashDraftInputV2,
gamma: int,
verify_num_draft_tokens: int,
cutoff_verify_lens: Optional[torch.Tensor] = None,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
return accept_sampling(
candidates=candidates,
target_logits=target_logits,
draft_probs=draft_probs,
sampling_info=sampling_info,
draft_input=draft_input,
gamma=gamma,
verify_num_draft_tokens=verify_num_draft_tokens,
cutoff_verify_lens=cutoff_verify_lens,
)
@classmethod
def triton(
cls,
*,
candidates: torch.Tensor,
target_logits: torch.Tensor,
draft_probs: torch.Tensor,
sampling_info,
draft_input: DFlashDraftInputV2,
gamma: int,
verify_num_draft_tokens: int,
cutoff_verify_lens: Optional[torch.Tensor] = None,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
return accept_sampling_triton(
candidates=candidates,
target_logits=target_logits,
draft_probs=draft_probs,
sampling_info=sampling_info,
draft_input=draft_input,
gamma=gamma,
verify_num_draft_tokens=verify_num_draft_tokens,
cutoff_verify_lens=cutoff_verify_lens,
)
def _accept_sampling_core(
*,
candidates: torch.Tensor,
target_logits: torch.Tensor,
draft_probs: torch.Tensor,
sampling_info,
draft_input: DFlashDraftInputV2,
gamma: int,
verify_num_draft_tokens: int,
cutoff_verify_lens: Optional[torch.Tensor],
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
bs = candidates.shape[0]
device = candidates.device
if not sampling_info.need_top_k_sampling and not sampling_info.need_top_p_sampling:
target_probs = SoftmaxTemp.execute(
logits=target_logits,
temperatures=sampling_info.temperatures,
rows_per_request=verify_num_draft_tokens,
).view(bs, verify_num_draft_tokens, -1)
else:
target_probs = build_dflash_verify_target_probs(
next_token_logits=target_logits,
sampling_info=sampling_info,
draft_token_num=verify_num_draft_tokens,
bs=bs,
max_top_k=draft_input.max_top_k,
uniform_top_k_value=draft_input.uniform_top_k_value,
)
(
retrieve_index,
retrieve_next_token,
retrieve_next_sibling,
predicts,
accept_index,
accept_token_num,
) = _get_or_create_chain_verify_buffers(
bs=bs,
draft_token_num=verify_num_draft_tokens,
device=device,
)
uniform_samples = torch.rand((bs, gamma), dtype=torch.float32, device=device)
uniform_samples_final = torch.rand((bs,), dtype=torch.float32, device=device)
chain_speculative_sampling_triton(
predicts=predicts,
accept_index=accept_index,
accept_token_num=accept_token_num,
candidates=candidates,
retrive_index=retrieve_index,
retrive_next_token=retrieve_next_token,
retrive_next_sibling=retrieve_next_sibling,
uniform_samples=uniform_samples,
uniform_samples_for_final_sampling=uniform_samples_final,
target_probs=target_probs,
draft_probs=draft_probs,
threshold_single=1.0,
threshold_acc=1.0,
deterministic=True,
)
correct_len = accept_token_num
if cutoff_verify_lens is not None:
correct_len, cap_trim_lens = CapCorrectLen.execute(
correct_len=correct_len, verify_lens=cutoff_verify_lens
)
else:
cap_trim_lens = torch.zeros_like(correct_len)
return correct_len, cap_trim_lens, accept_index, predicts
def accept_sampling(
*,
candidates: torch.Tensor,
target_logits: torch.Tensor,
draft_probs: torch.Tensor,
sampling_info,
draft_input: DFlashDraftInputV2,
gamma: int,
verify_num_draft_tokens: int,
cutoff_verify_lens: Optional[torch.Tensor] = None,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
bs = candidates.shape[0]
device = candidates.device
correct_len, cap_trim_lens, accept_index, predicts = _accept_sampling_core(
candidates=candidates,
target_logits=target_logits,
draft_probs=draft_probs,
sampling_info=sampling_info,
draft_input=draft_input,
gamma=gamma,
verify_num_draft_tokens=verify_num_draft_tokens,
cutoff_verify_lens=cutoff_verify_lens,
)
row_ids = torch.arange(bs, dtype=torch.long, device=device)
accept_pos = accept_index[row_ids, correct_len.to(torch.long)].to(torch.long)
bonus = predicts[accept_pos].to(torch.int64)
return correct_len, bonus, cap_trim_lens
@triton.jit
def _gather_two_level_bonus_kernel(
accept_index_ptr,
predicts_ptr,
correct_len_ptr,
out_ptr,
cols,
n,
BLOCK: tl.constexpr,
):
offs = tl.program_id(0) * BLOCK + tl.arange(0, BLOCK)
mask = offs < n
cl = tl.load(correct_len_ptr + offs, mask=mask, other=0).to(tl.int64)
accept_pos = tl.load(accept_index_ptr + offs * cols + cl, mask=mask, other=0).to(
tl.int64
)
bonus = tl.load(predicts_ptr + accept_pos, mask=mask, other=0)
tl.store(out_ptr + offs, bonus.to(tl.int64), mask=mask)
def gather_two_level_bonus_triton(
*,
accept_index: torch.Tensor,
predicts: torch.Tensor,
correct_len: torch.Tensor,
) -> torch.Tensor:
bs, cols = accept_index.shape
accept_index = accept_index.contiguous()
predicts = predicts.contiguous()
correct_len = correct_len.contiguous()
out = torch.empty(bs, dtype=torch.int64, device=accept_index.device)
BLOCK = 256
grid = (triton.cdiv(bs, BLOCK),)
_gather_two_level_bonus_kernel[grid](
accept_index, predicts, correct_len, out, cols, bs, BLOCK=BLOCK
)
return out
def accept_sampling_triton(
*,
candidates: torch.Tensor,
target_logits: torch.Tensor,
draft_probs: torch.Tensor,
sampling_info,
draft_input: DFlashDraftInputV2,
gamma: int,
verify_num_draft_tokens: int,
cutoff_verify_lens: Optional[torch.Tensor] = None,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
correct_len, cap_trim_lens, accept_index, predicts = _accept_sampling_core(
candidates=candidates,
target_logits=target_logits,
draft_probs=draft_probs,
sampling_info=sampling_info,
draft_input=draft_input,
gamma=gamma,
verify_num_draft_tokens=verify_num_draft_tokens,
cutoff_verify_lens=cutoff_verify_lens,
)
bonus = gather_two_level_bonus_triton(
accept_index=accept_index, predicts=predicts, correct_len=correct_len
)
return correct_len, bonus, cap_trim_lens
try:
from flashinfer.sampling import softmax as _flashinfer_softmax
except ImportError:
_flashinfer_softmax = None
class SoftmaxTemp:
@classmethod
def execute(cls, *args, **kwargs) -> torch.Tensor:
if not inputs_on_cuda(*args, **kwargs):
return cls.torch(*args, **kwargs)
if _flashinfer_softmax is not None:
return cls.flashinfer(*args, **kwargs)
return cls.triton(*args, **kwargs)
@classmethod
def torch(
cls,
*,
logits: torch.Tensor,
temperatures: torch.Tensor,
rows_per_request: int,
) -> torch.Tensor:
return softmax_temp(
logits=logits,
temperatures=temperatures,
rows_per_request=rows_per_request,
)
@classmethod
def triton(
cls,
*,
logits: torch.Tensor,
temperatures: torch.Tensor,
rows_per_request: int,
) -> torch.Tensor:
return softmax_temp_triton(
logits=logits,
temperatures=temperatures,
rows_per_request=rows_per_request,
)
@classmethod
def flashinfer(
cls,
*,
logits: torch.Tensor,
temperatures: torch.Tensor,
rows_per_request: int,
) -> torch.Tensor:
return softmax_temp_flashinfer(
logits=logits,
temperatures=temperatures,
rows_per_request=rows_per_request,
)
def softmax_temp(
*,
logits: torch.Tensor,
temperatures: torch.Tensor,
rows_per_request: int,
) -> torch.Tensor:
num_rows = logits.shape[0]
bs = num_rows // rows_per_request
assert (
bs * rows_per_request == num_rows
), f"num_rows {num_rows} not divisible by rows_per_request {rows_per_request}"
temp_per_row = torch.repeat_interleave(
temperatures.reshape(bs).to(torch.float32), rows_per_request, dim=0
)
scaled = logits.to(torch.float32) / temp_per_row[:, None]
return torch.softmax(scaled, dim=-1)
@triton.jit
def _softmax_temp_kernel(
logits_ptr,
temp_ptr,
out_ptr,
vocab,
rows_per_request,
logits_row_stride,
BLOCK_V: tl.constexpr,
):
row = tl.program_id(0)
temp = tl.load(temp_ptr + row // rows_per_request).to(tl.float32)
base = logits_ptr + row.to(tl.int64) * logits_row_stride
out_base = out_ptr + row.to(tl.int64) * vocab
row_max = -float("inf")
for v0 in range(0, vocab, BLOCK_V):
offs = v0 + tl.arange(0, BLOCK_V)
vmask = offs < vocab
x = tl.load(base + offs, mask=vmask, other=-float("inf")).to(tl.float32)
x = x / temp
row_max = tl.maximum(row_max, tl.max(x, axis=0))
sum_exp = 0.0
for v0 in range(0, vocab, BLOCK_V):
offs = v0 + tl.arange(0, BLOCK_V)
vmask = offs < vocab
x = tl.load(base + offs, mask=vmask, other=-float("inf")).to(tl.float32)
x = x / temp
e = tl.exp(x - row_max)
e = tl.where(vmask, e, 0.0)
sum_exp += tl.sum(e, axis=0)
for v0 in range(0, vocab, BLOCK_V):
offs = v0 + tl.arange(0, BLOCK_V)
vmask = offs < vocab
x = tl.load(base + offs, mask=vmask, other=-float("inf")).to(tl.float32)
x = x / temp
e = tl.exp(x - row_max)
tl.store(out_base + offs, e / sum_exp, mask=vmask)
def softmax_temp_triton(
*,
logits: torch.Tensor,
temperatures: torch.Tensor,
rows_per_request: int,
) -> torch.Tensor:
num_rows, vocab = logits.shape[0], logits.shape[-1]
bs = num_rows // rows_per_request
assert (
bs * rows_per_request == num_rows
), f"num_rows {num_rows} not divisible by rows_per_request {rows_per_request}"
temperatures = temperatures.reshape(bs).to(torch.float32).contiguous()
out = torch.empty((num_rows, vocab), dtype=torch.float32, device=logits.device)
BLOCK_V = 4096
_softmax_temp_kernel[(num_rows,)](
logits,
temperatures,
out,
vocab,
rows_per_request,
logits.stride(0),
BLOCK_V=BLOCK_V,
)
return out
def softmax_temp_flashinfer(
*,
logits: torch.Tensor,
temperatures: torch.Tensor,
rows_per_request: int,
) -> torch.Tensor:
if _flashinfer_softmax is None:
raise RuntimeError(
"softmax_temp_flashinfer requires flashinfer.sampling.softmax, "
"which is unavailable in this environment"
)
num_rows, vocab = logits.shape[0], logits.shape[-1]
bs = num_rows // rows_per_request
assert (
bs * rows_per_request == num_rows
), f"num_rows {num_rows} not divisible by rows_per_request {rows_per_request}"
temp_per_row = torch.repeat_interleave(
temperatures.reshape(bs).to(torch.float32), rows_per_request, dim=0
).contiguous()
logits_2d = logits.to(torch.float32).contiguous()
return _flashinfer_softmax(logits=logits_2d, temperature=temp_per_row)
class MixedAcceptSelectResult(msgspec.Struct):
correct_len: torch.Tensor
bonus: torch.Tensor
cap_trim_lens: torch.Tensor
class SelectMixedAccept:
@classmethod
def execute(cls, *args, **kwargs) -> MixedAcceptSelectResult:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
greedy_mask: torch.Tensor,
greedy_len: torch.Tensor,
greedy_bonus: torch.Tensor,
greedy_trim: torch.Tensor,
sampling_len: torch.Tensor,
sampling_bonus: torch.Tensor,
sampling_trim: torch.Tensor,
) -> MixedAcceptSelectResult:
return select_mixed_accept(
greedy_mask=greedy_mask,
greedy_len=greedy_len,
greedy_bonus=greedy_bonus,
greedy_trim=greedy_trim,
sampling_len=sampling_len,
sampling_bonus=sampling_bonus,
sampling_trim=sampling_trim,
)
@classmethod
def triton(
cls,
*,
greedy_mask: torch.Tensor,
greedy_len: torch.Tensor,
greedy_bonus: torch.Tensor,
greedy_trim: torch.Tensor,
sampling_len: torch.Tensor,
sampling_bonus: torch.Tensor,
sampling_trim: torch.Tensor,
) -> MixedAcceptSelectResult:
return select_mixed_accept_triton(
greedy_mask=greedy_mask,
greedy_len=greedy_len,
greedy_bonus=greedy_bonus,
greedy_trim=greedy_trim,
sampling_len=sampling_len,
sampling_bonus=sampling_bonus,
sampling_trim=sampling_trim,
)
def select_mixed_accept(
*,
greedy_mask: torch.Tensor,
greedy_len: torch.Tensor,
greedy_bonus: torch.Tensor,
greedy_trim: torch.Tensor,
sampling_len: torch.Tensor,
sampling_bonus: torch.Tensor,
sampling_trim: torch.Tensor,
) -> MixedAcceptSelectResult:
correct_len = torch.where(
greedy_mask, greedy_len.to(sampling_len.dtype), sampling_len
)
bonus = torch.where(greedy_mask, greedy_bonus, sampling_bonus)
cap_trim_lens = torch.where(
greedy_mask, greedy_trim.to(sampling_trim.dtype), sampling_trim
)
return MixedAcceptSelectResult(
correct_len=correct_len, bonus=bonus, cap_trim_lens=cap_trim_lens
)
@triton.jit
def _mixed_accept_select_kernel(
greedy_mask_ptr,
greedy_len_ptr,
greedy_bonus_ptr,
greedy_trim_ptr,
sampling_len_ptr,
sampling_bonus_ptr,
sampling_trim_ptr,
correct_len_ptr,
bonus_ptr,
cap_trim_ptr,
bs,
BLOCK: tl.constexpr,
):
offs = tl.program_id(0) * BLOCK + tl.arange(0, BLOCK)
mask = offs < bs
is_greedy = tl.load(greedy_mask_ptr + offs, mask=mask, other=0) != 0
g_len = tl.load(greedy_len_ptr + offs, mask=mask, other=0)
s_len = tl.load(sampling_len_ptr + offs, mask=mask, other=0)
tl.store(correct_len_ptr + offs, tl.where(is_greedy, g_len, s_len), mask=mask)
g_bonus = tl.load(greedy_bonus_ptr + offs, mask=mask, other=0)
s_bonus = tl.load(sampling_bonus_ptr + offs, mask=mask, other=0)
tl.store(bonus_ptr + offs, tl.where(is_greedy, g_bonus, s_bonus), mask=mask)
g_trim = tl.load(greedy_trim_ptr + offs, mask=mask, other=0)
s_trim = tl.load(sampling_trim_ptr + offs, mask=mask, other=0)
tl.store(cap_trim_ptr + offs, tl.where(is_greedy, g_trim, s_trim), mask=mask)
def select_mixed_accept_triton(
*,
greedy_mask: torch.Tensor,
greedy_len: torch.Tensor,
greedy_bonus: torch.Tensor,
greedy_trim: torch.Tensor,
sampling_len: torch.Tensor,
sampling_bonus: torch.Tensor,
sampling_trim: torch.Tensor,
) -> MixedAcceptSelectResult:
bs = greedy_mask.shape[0]
device = greedy_mask.device
correct_len = torch.empty(bs, dtype=sampling_len.dtype, device=device)
bonus = torch.empty(bs, dtype=sampling_bonus.dtype, device=device)
cap_trim_lens = torch.empty(bs, dtype=sampling_trim.dtype, device=device)
BLOCK = 256
_mixed_accept_select_kernel[(triton.cdiv(bs, BLOCK),)](
greedy_mask,
greedy_len,
greedy_bonus,
greedy_trim,
sampling_len,
sampling_bonus,
sampling_trim,
correct_len,
bonus,
cap_trim_lens,
bs,
BLOCK=BLOCK,
)
return MixedAcceptSelectResult(
correct_len=correct_len, bonus=bonus, cap_trim_lens=cap_trim_lens
)
class AcceptGreedy:
@classmethod
def execute(
cls, *args, **kwargs
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
candidates: torch.Tensor,
target_logits: torch.Tensor,
verify_num_draft_tokens: int,
cutoff_verify_lens: Optional[torch.Tensor] = None,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
return accept_greedy(
candidates=candidates,
target_logits=target_logits,
verify_num_draft_tokens=verify_num_draft_tokens,
cutoff_verify_lens=cutoff_verify_lens,
)
@classmethod
def triton(
cls,
*,
candidates: torch.Tensor,
target_logits: torch.Tensor,
verify_num_draft_tokens: int,
cutoff_verify_lens: Optional[torch.Tensor] = None,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
return accept_greedy_triton(
candidates=candidates,
target_logits=target_logits,
verify_num_draft_tokens=verify_num_draft_tokens,
cutoff_verify_lens=cutoff_verify_lens,
)
def accept_greedy(
*,
candidates: torch.Tensor,
target_logits: torch.Tensor,
verify_num_draft_tokens: int,
cutoff_verify_lens: Optional[torch.Tensor] = None,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
bs = candidates.shape[0]
target_predict = torch.argmax(target_logits, dim=-1).view(
bs, verify_num_draft_tokens
)
correct_len, bonus = compute_dflash_correct_drafts_and_bonus(
candidates=candidates,
target_predict=target_predict,
)
cap_trim_lens = torch.zeros_like(correct_len)
if cutoff_verify_lens is not None:
correct_len, cap_trim_lens = CapCorrectLen.execute(
correct_len=correct_len, verify_lens=cutoff_verify_lens
)
row_ids = torch.arange(bs, device=target_predict.device)
bonus = target_predict[row_ids, correct_len.to(torch.long)].to(torch.int64)
return correct_len, bonus, cap_trim_lens
@triton.jit
def _gather_row_bonus_kernel(
table_ptr,
idx_ptr,
out_ptr,
cols,
n,
BLOCK: tl.constexpr,
):
offs = tl.program_id(0) * BLOCK + tl.arange(0, BLOCK)
mask = offs < n
idx = tl.load(idx_ptr + offs, mask=mask, other=0).to(tl.int64)
val = tl.load(table_ptr + offs * cols + idx, mask=mask, other=0)
tl.store(out_ptr + offs, val.to(tl.int64), mask=mask)
def gather_row_bonus_triton(*, table: torch.Tensor, idx: torch.Tensor) -> torch.Tensor:
bs, cols = table.shape
table = table.contiguous()
idx = idx.contiguous()
out = torch.empty(bs, dtype=torch.int64, device=table.device)
BLOCK = 256
grid = (triton.cdiv(bs, BLOCK),)
_gather_row_bonus_kernel[grid](table, idx, out, cols, bs, BLOCK=BLOCK)
return out
def accept_greedy_triton(
*,
candidates: torch.Tensor,
target_logits: torch.Tensor,
verify_num_draft_tokens: int,
cutoff_verify_lens: Optional[torch.Tensor] = None,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
bs = candidates.shape[0]
target_predict = torch.argmax(target_logits, dim=-1).view(
bs, verify_num_draft_tokens
)
correct_len, bonus = compute_dflash_correct_drafts_and_bonus(
candidates=candidates,
target_predict=target_predict,
)
cap_trim_lens = torch.zeros_like(correct_len)
if cutoff_verify_lens is not None:
correct_len, cap_trim_lens = CapCorrectLen.execute(
correct_len=correct_len, verify_lens=cutoff_verify_lens
)
bonus = gather_row_bonus_triton(table=target_predict, idx=correct_len)
return correct_len, bonus, cap_trim_lens
class FinalizeAcceptLensResult(msgspec.Struct):
commit_lens: torch.Tensor
new_seq_lens: torch.Tensor
cap_trim_lens: torch.Tensor
class FinalizeAcceptLens:
@classmethod
def execute(cls, *args, **kwargs) -> FinalizeAcceptLensResult:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
correct_len: torch.Tensor,
cap_trim_lens: torch.Tensor,
prefix_lens: torch.Tensor,
) -> FinalizeAcceptLensResult:
return finalize_accept_lens(
correct_len=correct_len,
cap_trim_lens=cap_trim_lens,
prefix_lens=prefix_lens,
)
@classmethod
def triton(
cls,
*,
correct_len: torch.Tensor,
cap_trim_lens: torch.Tensor,
prefix_lens: torch.Tensor,
) -> FinalizeAcceptLensResult:
return finalize_accept_lens_triton(
correct_len=correct_len,
cap_trim_lens=cap_trim_lens,
prefix_lens=prefix_lens,
)
def finalize_accept_lens(
*,
correct_len: torch.Tensor,
cap_trim_lens: torch.Tensor,
prefix_lens: torch.Tensor,
) -> FinalizeAcceptLensResult:
commit_lens = correct_len.to(torch.int32) + 1
new_seq_lens = prefix_lens + commit_lens.to(prefix_lens.dtype)
return FinalizeAcceptLensResult(
commit_lens=commit_lens,
new_seq_lens=new_seq_lens,
cap_trim_lens=cap_trim_lens.to(torch.int32),
)
@triton.jit
def _finalize_accept_lens_kernel(
correct_len_ptr,
cap_trim_ptr,
prefix_lens_ptr,
commit_lens_ptr,
new_seq_lens_ptr,
cap_trim_out_ptr,
bs,
BLOCK: tl.constexpr,
):
offs = tl.program_id(0) * BLOCK + tl.arange(0, BLOCK)
mask = offs < bs
commit = tl.load(correct_len_ptr + offs, mask=mask, other=0).to(tl.int32) + 1
prefix = tl.load(prefix_lens_ptr + offs, mask=mask, other=0)
trim = tl.load(cap_trim_ptr + offs, mask=mask, other=0).to(tl.int32)
tl.store(commit_lens_ptr + offs, commit, mask=mask)
tl.store(new_seq_lens_ptr + offs, prefix + commit, mask=mask)
tl.store(cap_trim_out_ptr + offs, trim, mask=mask)
def finalize_accept_lens_triton(
*,
correct_len: torch.Tensor,
cap_trim_lens: torch.Tensor,
prefix_lens: torch.Tensor,
) -> FinalizeAcceptLensResult:
bs = correct_len.shape[0]
device = correct_len.device
commit_lens = torch.empty(bs, dtype=torch.int32, device=device)
new_seq_lens = torch.empty(bs, dtype=prefix_lens.dtype, device=device)
cap_trim_out = torch.empty(bs, dtype=torch.int32, device=device)
BLOCK = 256
_finalize_accept_lens_kernel[(triton.cdiv(bs, BLOCK),)](
correct_len,
cap_trim_lens,
prefix_lens,
commit_lens,
new_seq_lens,
cap_trim_out,
bs,
BLOCK=BLOCK,
)
return FinalizeAcceptLensResult(
commit_lens=commit_lens,
new_seq_lens=new_seq_lens,
cap_trim_lens=cap_trim_out,
)
class CapCorrectLen:
@classmethod
def execute(cls, *args, **kwargs) -> tuple[torch.Tensor, torch.Tensor]:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
correct_len: torch.Tensor,
verify_lens: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor]:
return cap_correct_len(
correct_len=correct_len,
verify_lens=verify_lens,
)
@classmethod
def triton(
cls,
*,
correct_len: torch.Tensor,
verify_lens: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor]:
return cap_correct_len_triton(
correct_len=correct_len,
verify_lens=verify_lens,
)
def cap_correct_len(
*,
correct_len: torch.Tensor,
verify_lens: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor]:
ell_r = (verify_lens.to(device=correct_len.device) - 1).to(correct_len.dtype)
capped = torch.minimum(correct_len, ell_r)
cap_trim_lens = correct_len - capped
return capped, cap_trim_lens
@triton.jit
def _cap_correct_len_kernel(
correct_len_ptr,
verify_lens_ptr,
capped_ptr,
trim_ptr,
n,
BLOCK: tl.constexpr,
):
pid = tl.program_id(0)
offs = pid * BLOCK + tl.arange(0, BLOCK)
mask = offs < n
cl = tl.load(correct_len_ptr + offs, mask=mask, other=0).to(tl.int64)
vl = tl.load(verify_lens_ptr + offs, mask=mask, other=0).to(tl.int64)
ell = vl - 1
capped = tl.minimum(cl, ell)
trim = cl - capped
tl.store(capped_ptr + offs, capped, mask=mask)
tl.store(trim_ptr + offs, trim, mask=mask)
def cap_correct_len_triton(
*,
correct_len: torch.Tensor,
verify_lens: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor]:
device = correct_len.device
correct_len = correct_len.contiguous()
verify_lens = verify_lens.to(device=device).contiguous()
n = correct_len.shape[0]
capped = torch.empty_like(correct_len)
trim = torch.empty_like(correct_len)
BLOCK = 1024
grid = (triton.cdiv(n, BLOCK),)
_cap_correct_len_kernel[grid](
correct_len, verify_lens, capped, trim, n, BLOCK=BLOCK
)
return capped, trim
@@ -0,0 +1,491 @@
from __future__ import annotations
from typing import Tuple
import msgspec
import torch
import triton
import triton.language as tl
from sglang.srt.speculative.dspark_components.kernels.dispatch import inputs_on_cuda
from sglang.srt.utils import ceil_align
class DsparkWindowGather(msgspec.Struct, frozen=True):
num_q: int
bs: int
context_lens: torch.Tensor
req_pool_indices_per_request: torch.Tensor
offsets: torch.Tensor
invalid: torch.Tensor
class ComputeDsparkWindowGather:
@classmethod
def execute(cls, *args, **kwargs) -> DsparkWindowGather:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
seq_lens_casual: torch.Tensor,
req_pool_indices_repeated: torch.Tensor,
block_size: int,
swa_window: int,
) -> DsparkWindowGather:
return compute_dspark_window_gather(
seq_lens_casual=seq_lens_casual,
req_pool_indices_repeated=req_pool_indices_repeated,
block_size=block_size,
swa_window=swa_window,
)
@classmethod
def triton(
cls,
*,
seq_lens_casual: torch.Tensor,
req_pool_indices_repeated: torch.Tensor,
block_size: int,
swa_window: int,
) -> DsparkWindowGather:
return compute_dspark_window_gather_triton(
seq_lens_casual=seq_lens_casual,
req_pool_indices_repeated=req_pool_indices_repeated,
block_size=block_size,
swa_window=swa_window,
)
class BuildDsparkSwaPageIndices:
@classmethod
def execute(cls, *args, **kwargs) -> Tuple[torch.Tensor, torch.Tensor]:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
req_to_token: torch.Tensor,
full_to_swa_mapping: torch.Tensor,
req_pool_indices_per_request: torch.Tensor,
offsets: torch.Tensor,
invalid: torch.Tensor,
out_loc: torch.Tensor,
context_lens: torch.Tensor,
block_size: int,
swa_window: int,
page_index_aligned_size: int,
) -> Tuple[torch.Tensor, torch.Tensor]:
return build_dspark_swa_page_indices(
req_to_token=req_to_token,
full_to_swa_mapping=full_to_swa_mapping,
req_pool_indices_per_request=req_pool_indices_per_request,
offsets=offsets,
invalid=invalid,
out_loc=out_loc,
context_lens=context_lens,
block_size=block_size,
swa_window=swa_window,
page_index_aligned_size=page_index_aligned_size,
)
@classmethod
def triton(
cls,
*,
req_to_token: torch.Tensor,
full_to_swa_mapping: torch.Tensor,
req_pool_indices_per_request: torch.Tensor,
offsets: torch.Tensor,
invalid: torch.Tensor,
out_loc: torch.Tensor,
context_lens: torch.Tensor,
block_size: int,
swa_window: int,
page_index_aligned_size: int,
) -> Tuple[torch.Tensor, torch.Tensor]:
return build_dspark_swa_page_indices_triton(
req_to_token=req_to_token,
full_to_swa_mapping=full_to_swa_mapping,
req_pool_indices_per_request=req_pool_indices_per_request,
offsets=offsets,
out_loc=out_loc,
context_lens=context_lens,
block_size=block_size,
swa_window=swa_window,
page_index_aligned_size=page_index_aligned_size,
)
def compute_dspark_window_gather(
*,
seq_lens_casual: torch.Tensor,
req_pool_indices_repeated: torch.Tensor,
block_size: int,
swa_window: int,
) -> DsparkWindowGather:
seq_lens_casual = seq_lens_casual.to(torch.int32)
num_q = seq_lens_casual.size(0)
assert num_q % block_size == 0, (
f"DSpark draft block forward must be uniform-gamma: num_q={num_q} not "
f"divisible by block_size={block_size}."
)
bs = num_q // block_size
device = seq_lens_casual.device
first_token = torch.arange(bs, device=device, dtype=torch.int64) * block_size
prefix_lens = (seq_lens_casual[first_token] - 1).to(torch.int32)
context_lens = torch.clamp(prefix_lens, max=swa_window).to(torch.int32)
req_pool_indices_per_request = req_pool_indices_repeated[first_token]
offsets = (
prefix_lens.to(torch.int64).unsqueeze(1)
- swa_window
+ torch.arange(swa_window, device=device, dtype=torch.int64).unsqueeze(0)
)
invalid = offsets < 0
offsets = offsets.clamp(min=0)
return DsparkWindowGather(
num_q=num_q,
bs=bs,
context_lens=context_lens,
req_pool_indices_per_request=req_pool_indices_per_request,
offsets=offsets,
invalid=invalid,
)
@triton.jit
def _window_gather_kernel(
seq_lens_casual_ptr,
req_pool_rep_ptr,
context_lens_ptr,
req_pool_out_ptr,
offsets_ptr,
invalid_ptr,
block_size,
swa_window,
W_BLOCK: tl.constexpr,
):
i = tl.program_id(0)
ft = i * block_size
prefix = tl.load(seq_lens_casual_ptr + ft).to(tl.int64) - 1
tl.store(context_lens_ptr + i, tl.minimum(prefix, swa_window).to(tl.int32))
tl.store(req_pool_out_ptr + i, tl.load(req_pool_rep_ptr + ft))
col = tl.arange(0, W_BLOCK)
cmask = col < swa_window
off = prefix - swa_window + col
tl.store(invalid_ptr + i * swa_window + col, off < 0, mask=cmask)
tl.store(offsets_ptr + i * swa_window + col, tl.maximum(off, 0), mask=cmask)
def compute_dspark_window_gather_triton(
*,
seq_lens_casual: torch.Tensor,
req_pool_indices_repeated: torch.Tensor,
block_size: int,
swa_window: int,
) -> DsparkWindowGather:
seq_lens_casual = seq_lens_casual.to(torch.int32).contiguous()
num_q = seq_lens_casual.size(0)
assert num_q % block_size == 0, (
f"DSpark draft block forward must be uniform-gamma: num_q={num_q} not "
f"divisible by block_size={block_size}."
)
bs = num_q // block_size
device = seq_lens_casual.device
req_pool_indices_repeated = req_pool_indices_repeated.to(device=device).contiguous()
context_lens = torch.empty(bs, dtype=torch.int32, device=device)
req_pool_out = torch.empty(bs, dtype=req_pool_indices_repeated.dtype, device=device)
offsets = torch.empty((bs, swa_window), dtype=torch.int64, device=device)
invalid = torch.empty((bs, swa_window), dtype=torch.bool, device=device)
W_BLOCK = triton.next_power_of_2(swa_window)
_window_gather_kernel[(bs,)](
seq_lens_casual,
req_pool_indices_repeated,
context_lens,
req_pool_out,
offsets,
invalid,
block_size,
swa_window,
W_BLOCK=W_BLOCK,
)
return DsparkWindowGather(
num_q=num_q,
bs=bs,
context_lens=context_lens,
req_pool_indices_per_request=req_pool_out,
offsets=offsets,
invalid=invalid,
)
def build_dspark_swa_page_indices(
*,
req_to_token: torch.Tensor,
full_to_swa_mapping: torch.Tensor,
req_pool_indices_per_request: torch.Tensor,
offsets: torch.Tensor,
invalid: torch.Tensor,
out_loc: torch.Tensor,
context_lens: torch.Tensor,
block_size: int,
swa_window: int,
page_index_aligned_size: int,
) -> Tuple[torch.Tensor, torch.Tensor]:
if offsets.ndim != 2 or offsets.shape[1] != swa_window:
raise ValueError(
"offsets must be [bs, swa_window]; "
f"got shape={tuple(offsets.shape)} (swa_window={swa_window})."
)
bs = offsets.shape[0]
device = offsets.device
context_lens = context_lens.to(device=device, dtype=torch.int32)
window_full_locs = req_to_token[
req_pool_indices_per_request[:, None].to(torch.int64), offsets
]
window_full_locs = window_full_locs.masked_fill(invalid, 0)
window_swa_locs = full_to_swa_mapping[window_full_locs].to(torch.int32)
window_swa_locs = window_swa_locs.masked_fill(invalid, -1)
block_full_locs = out_loc[: bs * block_size].view(bs, block_size)
block_swa_locs = full_to_swa_mapping[block_full_locs].to(torch.int32)
target_width = ceil_align(swa_window + block_size, page_index_aligned_size)
swa_page_indices = _compact_dspark_window_then_block(
window_swa_locs=window_swa_locs,
block_swa_locs=block_swa_locs,
context_lens=context_lens,
target_width=target_width,
block_size=block_size,
swa_window=swa_window,
)
swa_page_indices = (
swa_page_indices.view(bs, 1, target_width)
.expand(bs, block_size, target_width)
.reshape(bs * block_size, target_width)
.contiguous()
)
swa_topk_lengths = (
(context_lens + block_size)
.view(bs, 1)
.expand(bs, block_size)
.reshape(bs * block_size)
.contiguous()
.to(torch.int32)
)
return swa_page_indices, swa_topk_lengths
def _compact_dspark_window_then_block(
*,
window_swa_locs: torch.Tensor,
block_swa_locs: torch.Tensor,
context_lens: torch.Tensor,
target_width: int,
block_size: int,
swa_window: int,
) -> torch.Tensor:
bs = window_swa_locs.shape[0]
device = window_swa_locs.device
out = torch.full((bs, target_width), -1, dtype=torch.int32, device=device)
j = torch.arange(swa_window, device=device, dtype=torch.int32).view(1, -1)
shift = (swa_window - context_lens.view(-1, 1)).to(torch.int32)
src_col = (shift + j).clamp_(min=0, max=swa_window - 1).to(torch.int64)
gathered = torch.gather(window_swa_locs, dim=1, index=src_col)
valid = j < context_lens.view(-1, 1)
out[:, :swa_window] = torch.where(valid, gathered, -1)
block_col = context_lens.view(-1, 1) + torch.arange(
block_size, device=device, dtype=torch.int32
).view(1, -1)
block_rows = torch.arange(bs, device=device).view(-1, 1).expand(-1, block_size)
out[block_rows, block_col] = block_swa_locs
return out
@triton.jit
def _swa_page_indices_kernel(
req_to_token_ptr,
full_to_swa_ptr,
req_pool_ptr,
offsets_ptr,
out_loc_ptr,
context_lens_ptr,
out_ptr,
topk_ptr,
rt_stride,
swa_window,
block_size,
target_width,
TW_BLOCK: tl.constexpr,
):
q = tl.program_id(0)
i = q // block_size
cl = tl.load(context_lens_ptr + i)
rp = tl.load(req_pool_ptr + i).to(tl.int64)
k = tl.arange(0, TW_BLOCK)
kmask = k < target_width
in_window = k < cl
src_col = tl.minimum(tl.maximum((swa_window - cl) + k, 0), swa_window - 1)
wmask = kmask & in_window
off = tl.load(offsets_ptr + i * swa_window + src_col, mask=wmask, other=0).to(
tl.int64
)
win_full = tl.load(req_to_token_ptr + rp * rt_stride + off, mask=wmask, other=0).to(
tl.int64
)
win_swa = tl.load(full_to_swa_ptr + win_full, mask=wmask, other=-1).to(tl.int32)
in_block = (k >= cl) & (k < cl + block_size)
bmask = kmask & in_block
bcol = tl.maximum(k - cl, 0)
blk_full = tl.load(out_loc_ptr + i * block_size + bcol, mask=bmask, other=0).to(
tl.int64
)
blk_swa = tl.load(full_to_swa_ptr + blk_full, mask=bmask, other=-1).to(tl.int32)
val = tl.where(in_window, win_swa, tl.where(in_block, blk_swa, -1))
tl.store(out_ptr + q * target_width + k, val.to(tl.int32), mask=kmask)
tl.store(topk_ptr + q, (cl + block_size).to(tl.int32))
def build_dspark_swa_page_indices_triton(
*,
req_to_token: torch.Tensor,
full_to_swa_mapping: torch.Tensor,
req_pool_indices_per_request: torch.Tensor,
offsets: torch.Tensor,
out_loc: torch.Tensor,
context_lens: torch.Tensor,
block_size: int,
swa_window: int,
page_index_aligned_size: int,
) -> Tuple[torch.Tensor, torch.Tensor]:
if offsets.ndim != 2 or offsets.shape[1] != swa_window:
raise ValueError(
"offsets must be [bs, swa_window]; "
f"got shape={tuple(offsets.shape)} (swa_window={swa_window})."
)
bs = offsets.shape[0]
device = offsets.device
req_pool = req_pool_indices_per_request.to(device=device).contiguous()
offsets = offsets.to(torch.int64).contiguous()
out_loc = out_loc[: bs * block_size].contiguous()
context_lens = context_lens.to(device=device, dtype=torch.int32).contiguous()
rt_stride = req_to_token.stride(0)
target_width = ceil_align(swa_window + block_size, page_index_aligned_size)
n_q = bs * block_size
swa_page_indices = torch.empty(
(n_q, target_width), dtype=torch.int32, device=device
)
swa_topk_lengths = torch.empty(n_q, dtype=torch.int32, device=device)
TW_BLOCK = triton.next_power_of_2(target_width)
_swa_page_indices_kernel[(n_q,)](
req_to_token,
full_to_swa_mapping,
req_pool,
offsets,
out_loc,
context_lens,
swa_page_indices,
swa_topk_lengths,
rt_stride,
swa_window,
block_size,
target_width,
TW_BLOCK=TW_BLOCK,
)
return swa_page_indices, swa_topk_lengths
class BuildBlockSeqLensCausal:
@classmethod
def execute(cls, *args, **kwargs) -> torch.Tensor:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
seq_lens: torch.Tensor,
block_size: int,
device: torch.device,
) -> torch.Tensor:
return build_block_seq_lens_causal(
seq_lens=seq_lens,
block_size=block_size,
device=device,
)
@classmethod
def triton(
cls,
*,
seq_lens: torch.Tensor,
block_size: int,
device: torch.device,
) -> torch.Tensor:
return build_block_seq_lens_causal_triton(
seq_lens=seq_lens,
block_size=block_size,
device=device,
)
def build_block_seq_lens_causal(
*,
seq_lens: torch.Tensor,
block_size: int,
device: torch.device,
) -> torch.Tensor:
prefix = seq_lens.to(torch.int32)
steps = torch.arange(1, block_size + 1, device=device, dtype=torch.int32)
return (prefix[:, None] + steps[None, :]).reshape(-1)
@triton.jit
def _block_seq_lens_casual_kernel(
seq_lens_ptr,
out_ptr,
block_size,
n_out,
BLOCK: tl.constexpr,
):
pid = tl.program_id(0)
offs = pid * BLOCK + tl.arange(0, BLOCK)
mask = offs < n_out
row = offs // block_size
col = offs % block_size
prefix = tl.load(seq_lens_ptr + row, mask=mask, other=0)
tl.store(out_ptr + offs, (prefix + col + 1).to(tl.int32), mask=mask)
def build_block_seq_lens_causal_triton(
*,
seq_lens: torch.Tensor,
block_size: int,
device: torch.device,
) -> torch.Tensor:
seq_lens = seq_lens.to(device=device, dtype=torch.int64).contiguous()
n_rows = seq_lens.shape[0]
n_out = n_rows * block_size
out = torch.empty(n_out, dtype=torch.int32, device=device)
BLOCK = 256
grid = (triton.cdiv(n_out, BLOCK),)
_block_seq_lens_casual_kernel[grid](seq_lens, out, block_size, n_out, BLOCK=BLOCK)
return out
@@ -0,0 +1,443 @@
from __future__ import annotations
from typing import Optional
import msgspec
import torch
import torch.nn.functional as F
import triton
import triton.language as tl
from sglang.srt.speculative.dspark_components.kernels.dispatch import inputs_on_cuda
_BLOCK_V = 1024
_IDX_SENTINEL = tl.constexpr(2147483647)
class SampleStepTokens:
@classmethod
def execute(
cls,
*,
step_logits: torch.Tensor,
temperatures: torch.Tensor,
greedy_mask: torch.Tensor,
exp_noise: torch.Tensor,
) -> torch.Tensor:
if step_logits.is_cuda:
return cls.triton(
step_logits=step_logits,
temperatures=temperatures,
greedy_mask=greedy_mask,
exp_noise=exp_noise,
)
return cls.torch(
step_logits=step_logits,
temperatures=temperatures,
greedy_mask=greedy_mask,
exp_noise=exp_noise,
)
@classmethod
def torch(
cls,
*,
step_logits: torch.Tensor,
temperatures: torch.Tensor,
greedy_mask: torch.Tensor,
exp_noise: torch.Tensor,
) -> torch.Tensor:
return sample_step_tokens(
step_logits=step_logits,
temperatures=temperatures,
greedy_mask=greedy_mask,
exp_noise=exp_noise,
)
@classmethod
def triton(
cls,
*,
step_logits: torch.Tensor,
temperatures: torch.Tensor,
greedy_mask: torch.Tensor,
exp_noise: torch.Tensor,
) -> torch.Tensor:
return sample_step_tokens_triton(
step_logits=step_logits,
temperatures=temperatures,
greedy_mask=greedy_mask,
exp_noise=exp_noise,
)
def sample_step_tokens(
*,
step_logits: torch.Tensor,
temperatures: torch.Tensor,
greedy_mask: torch.Tensor,
exp_noise: torch.Tensor,
) -> torch.Tensor:
probs = torch.softmax(step_logits.float() / temperatures[:, None], dim=-1)
noise = torch.where(greedy_mask[:, None], 1.0, exp_noise)
return probs.div_(noise).argmax(dim=-1)
@triton.jit
def _online_partial_kernel(
logits_ptr,
temperatures_ptr,
greedy_mask_ptr,
exp_noise_ptr,
tile_max_ptr,
partial_key_ptr,
partial_idx_ptr,
V,
stride_row,
n_tiles,
BLOCK_V: tl.constexpr,
):
row = tl.program_id(0)
tile = tl.program_id(1)
offs = tile * BLOCK_V + tl.arange(0, BLOCK_V)
mask = offs < V
logits = tl.load(
logits_ptr + row * stride_row + offs, mask=mask, other=float("-inf")
).to(tl.float32)
temperature = tl.load(temperatures_ptr + row)
s = logits / temperature
tile_max = tl.max(s, axis=0)
greedy = tl.load(greedy_mask_ptr + row) != 0
noise = tl.load(exp_noise_ptr + row * V + offs, mask=mask, other=1.0)
denom = tl.where(greedy, 1.0, noise)
key = tl.exp(s - tile_max) / denom
key = tl.where(mask, key, -1.0)
tile_best = tl.max(key, axis=0)
idx = tl.where(key == tile_best, offs, _IDX_SENTINEL)
tl.store(tile_max_ptr + row * n_tiles + tile, tile_max)
tl.store(partial_key_ptr + row * n_tiles + tile, tile_best)
tl.store(partial_idx_ptr + row * n_tiles + tile, tl.min(idx, axis=0))
@triton.jit
def _online_combine_kernel(
tile_max_ptr,
partial_key_ptr,
partial_idx_ptr,
next_tokens_ptr,
n_tiles,
BLOCK_TILES: tl.constexpr,
):
row = tl.program_id(0)
offs = tl.arange(0, BLOCK_TILES)
mask = offs < n_tiles
tile_max = tl.load(
tile_max_ptr + row * n_tiles + offs, mask=mask, other=float("-inf")
)
keys = tl.load(partial_key_ptr + row * n_tiles + offs, mask=mask, other=-1.0)
idxs = tl.load(
partial_idx_ptr + row * n_tiles + offs, mask=mask, other=_IDX_SENTINEL
)
global_max = tl.max(tile_max, axis=0)
rescaled = keys * tl.exp(tile_max - global_max)
rescaled = tl.where(mask, rescaled, -1.0)
best = tl.max(rescaled, axis=0)
cand = tl.where(rescaled == best, idxs, _IDX_SENTINEL)
tl.store(next_tokens_ptr + row, tl.min(cand, axis=0).to(tl.int64))
def sample_step_tokens_triton(
*,
step_logits: torch.Tensor,
temperatures: torch.Tensor,
greedy_mask: torch.Tensor,
exp_noise: torch.Tensor,
) -> torch.Tensor:
bs, V = step_logits.shape
device = step_logits.device
assert step_logits.stride(1) == 1, "step_logits rows must be contiguous"
stride_row = step_logits.stride(0)
temperatures = temperatures.to(torch.float32).contiguous()
greedy_mask = greedy_mask.to(torch.int32).contiguous()
exp_noise = exp_noise.to(torch.float32).contiguous()
n_tiles = triton.cdiv(V, _BLOCK_V)
block_tiles = triton.next_power_of_2(n_tiles)
tile_max = torch.empty((bs, n_tiles), dtype=torch.float32, device=device)
partial_key = torch.empty((bs, n_tiles), dtype=torch.float32, device=device)
partial_idx = torch.empty((bs, n_tiles), dtype=torch.int32, device=device)
next_tokens = torch.empty((bs,), dtype=torch.int64, device=device)
tile_grid = (bs, n_tiles)
row_grid = (bs,)
_online_partial_kernel[tile_grid](
step_logits,
temperatures,
greedy_mask,
exp_noise,
tile_max,
partial_key,
partial_idx,
V,
stride_row,
n_tiles,
BLOCK_V=_BLOCK_V,
)
_online_combine_kernel[row_grid](
tile_max,
partial_key,
partial_idx,
next_tokens,
n_tiles,
BLOCK_TILES=block_tiles,
)
return next_tokens
_STACKED_WEIGHT_CACHE: dict[int, _StackedWkvWeight] = {}
class CommitKvProj:
@classmethod
def execute(
cls,
*,
main_x: torch.Tensor,
wkv_linears: list[torch.nn.Module],
) -> list[torch.Tensor]:
if main_x.is_cuda and _fused_commit_kv_proj_supported(wkv_linears=wkv_linears):
return cls.triton(main_x=main_x, wkv_linears=wkv_linears)
return cls.torch(main_x=main_x, wkv_linears=wkv_linears)
@classmethod
def torch(
cls,
*,
main_x: torch.Tensor,
wkv_linears: list[torch.nn.Module],
) -> list[torch.Tensor]:
return commit_kv_proj(main_x=main_x, wkv_linears=wkv_linears)
@classmethod
def triton(
cls,
*,
main_x: torch.Tensor,
wkv_linears: list[torch.nn.Module],
) -> list[torch.Tensor]:
return commit_kv_proj_fused(main_x=main_x, wkv_linears=wkv_linears)
def commit_kv_proj(
*,
main_x: torch.Tensor,
wkv_linears: list[torch.nn.Module],
) -> list[torch.Tensor]:
return [linear(main_x)[0] for linear in wkv_linears]
def commit_kv_proj_fused(
*,
main_x: torch.Tensor,
wkv_linears: list[torch.nn.Module],
) -> list[torch.Tensor]:
num_stages = len(wkv_linears)
stacked = _stacked_wkv_weight(wkv_linears=wkv_linears)
if stacked.fp8_scale is not None:
quant_method = wkv_linears[0].quant_method
kv_all = quant_method.w8a8_block_fp8_linear(
input=main_x,
weight=stacked.weight,
block_size=quant_method.quant_config.weight_block_size,
weight_scale=stacked.fp8_scale,
input_scale=None,
bias=None,
)
else:
kv_all = torch.nn.functional.linear(main_x, stacked.weight)
head_dim = kv_all.shape[-1] // num_stages
return [
kv_all[:, i * head_dim : (i + 1) * head_dim].contiguous()
for i in range(num_stages)
]
class _StackedWkvWeight(msgspec.Struct):
weight: torch.Tensor
fp8_scale: Optional[torch.Tensor]
def _stacked_wkv_weight(*, wkv_linears: list[torch.nn.Module]) -> _StackedWkvWeight:
key = id(wkv_linears[0])
cached = _STACKED_WEIGHT_CACHE.get(key)
if cached is None:
cached = _build_stacked_wkv_weight(wkv_linears=wkv_linears)
_STACKED_WEIGHT_CACHE[key] = cached
return cached
def _block_quant_stack_applies(*, wkv_linears: list[torch.nn.Module]) -> bool:
quant_method = wkv_linears[0].quant_method
block_quant = hasattr(quant_method, "block_quant") and quant_method.block_quant
if not (block_quant and hasattr(quant_method, "w8a8_block_fp8_linear")):
return False
block_out = quant_method.quant_config.weight_block_size[0]
return all(
linear.weight.dtype == torch.float8_e4m3fn
and linear.weight.shape[0] % block_out == 0
for linear in wkv_linears
)
def _dequant_supported(linear: torch.nn.Module) -> bool:
"""Mirrors the preconditions asserted in _dequant_linear_weight."""
weight = linear.weight
if weight.dtype in (torch.bfloat16, torch.float16, torch.float32):
return True
if weight.dtype != torch.float8_e4m3fn:
return False
block = 128
out_dim, in_dim = weight.shape
expected_scale_shape = (
(out_dim + block - 1) // block,
(in_dim + block - 1) // block,
)
return tuple(linear.weight_scale_inv.shape) == expected_scale_shape
def _fused_commit_kv_proj_supported(*, wkv_linears: list[torch.nn.Module]) -> bool:
"""Whether _build_stacked_wkv_weight can handle these weights; unsupported
quant schemes fall back to the per-linear torch path in execute()."""
if _block_quant_stack_applies(wkv_linears=wkv_linears):
return True
return all(_dequant_supported(linear) for linear in wkv_linears)
def _build_stacked_wkv_weight(
*, wkv_linears: list[torch.nn.Module]
) -> _StackedWkvWeight:
if _block_quant_stack_applies(wkv_linears=wkv_linears):
weight = torch.cat([linear.weight for linear in wkv_linears], dim=0)
if wkv_linears[0].weight_scale_inv.dtype == torch.int32:
from sglang.srt.layers.quantization.fp8_utils import (
inverse_transform_scale_ue8m0,
transform_scale_ue8m0,
)
sf_fp32 = torch.cat(
[
inverse_transform_scale_ue8m0(
linear.weight_scale_inv, mn=linear.weight.shape[0]
)
for linear in wkv_linears
],
dim=0,
)
scale = transform_scale_ue8m0(sf_fp32, mn=weight.shape[0])
return _StackedWkvWeight(weight=weight, fp8_scale=scale)
scale = torch.cat([linear.weight_scale_inv for linear in wkv_linears], dim=0)
if scale.dim() >= 2 and scale.stride(-2) != 1:
scale = scale.transpose(-2, -1).contiguous().transpose(-2, -1)
return _StackedWkvWeight(weight=weight, fp8_scale=scale)
weight = torch.cat(
[_dequant_linear_weight(linear) for linear in wkv_linears], dim=0
)
return _StackedWkvWeight(weight=weight, fp8_scale=None)
def _dequant_linear_weight(linear: torch.nn.Module) -> torch.Tensor:
weight = linear.weight
if weight.dtype in (torch.bfloat16, torch.float16, torch.float32):
return weight.to(torch.bfloat16)
assert weight.dtype == torch.float8_e4m3fn, (
f"unsupported wkv weight dtype {weight.dtype} for the fused commit kv proj; "
f"execute() should have routed this to the torch path "
f"(_fused_commit_kv_proj_supported)"
)
block = 128
scale = linear.weight_scale_inv
out_dim, in_dim = weight.shape
expected_scale_shape = (
(out_dim + block - 1) // block,
(in_dim + block - 1) // block,
)
assert tuple(scale.shape) == expected_scale_shape, (
f"wkv weight_scale_inv shape {tuple(scale.shape)} does not match the "
f"128x128 block grid {expected_scale_shape} for weight {tuple(weight.shape)}; "
f"execute() should have routed this to the torch path "
f"(_fused_commit_kv_proj_supported)"
)
scale_full = scale.repeat_interleave(block, dim=0)[:out_dim]
scale_full = scale_full.repeat_interleave(block, dim=1)[:, :in_dim]
return (weight.to(torch.float32) * scale_full.to(torch.float32)).to(torch.bfloat16)
_BLOCK = 1024
class BuildStepLocal:
@classmethod
def execute(cls, *args, **kwargs) -> torch.Tensor:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(cls, *, bias: torch.Tensor, base_local: torch.Tensor) -> torch.Tensor:
return build_step_local(bias=bias, base_local=base_local)
@classmethod
def triton(cls, *, bias: torch.Tensor, base_local: torch.Tensor) -> torch.Tensor:
return build_step_local_triton(bias=bias, base_local=base_local)
def build_step_local(*, bias: torch.Tensor, base_local: torch.Tensor) -> torch.Tensor:
per_partition = base_local.shape[-1]
pad = per_partition - bias.shape[-1]
padded = (
F.pad(bias.to(torch.float32), (0, pad)) if pad > 0 else bias.to(torch.float32)
)
return base_local + padded
@triton.jit
def _build_step_local_kernel(
bias_ptr,
base_ptr,
out_ptr,
org_width,
per_partition,
BLOCK: tl.constexpr,
):
row = tl.program_id(0)
tile = tl.program_id(1)
offs = tile * BLOCK + tl.arange(0, BLOCK)
mask = offs < per_partition
base = tl.load(base_ptr + row * per_partition + offs, mask=mask, other=0.0).to(
tl.float32
)
bias = tl.load(
bias_ptr + row * org_width + offs, mask=offs < org_width, other=0.0
).to(tl.float32)
tl.store(out_ptr + row * per_partition + offs, base + bias, mask=mask)
def build_step_local_triton(
*, bias: torch.Tensor, base_local: torch.Tensor
) -> torch.Tensor:
bs, per_partition = base_local.shape
org_width = bias.shape[-1]
base_local = base_local.contiguous()
bias = bias.contiguous()
out = torch.empty(
(bs, per_partition), dtype=torch.float32, device=base_local.device
)
grid = (bs, triton.cdiv(per_partition, _BLOCK))
_build_step_local_kernel[grid](
bias, base_local, out, org_width, per_partition, BLOCK=_BLOCK
)
return out
@@ -0,0 +1,260 @@
from __future__ import annotations
from typing import TYPE_CHECKING
import torch
import triton
import triton.language as tl
from sglang.srt.speculative.dspark_components.kernels.dispatch import (
inputs_on_cuda,
)
if TYPE_CHECKING:
from sglang.srt.speculative.dspark_components.dspark_planner import (
DSparkScheduleConfig,
)
class ScheduleVerifyLensTopk:
@classmethod
def execute(cls, *args, **kwargs) -> torch.Tensor:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
confidence: torch.Tensor,
budget: int,
cfg: DSparkScheduleConfig,
) -> torch.Tensor:
return schedule_verify_lens_topk(confidence=confidence, budget=budget, cfg=cfg)
@classmethod
def triton(
cls,
*,
confidence: torch.Tensor,
budget: int,
cfg: DSparkScheduleConfig,
) -> torch.Tensor:
return schedule_verify_lens_topk_triton(
confidence=confidence, budget=budget, cfg=cfg
)
def compute_sort_survival(confidence: torch.Tensor) -> torch.Tensor:
return torch.cumprod(confidence.to(torch.float32), dim=1)
def schedule_verify_lens_topk(
*,
confidence: torch.Tensor,
budget: int,
cfg: DSparkScheduleConfig,
) -> torch.Tensor:
return schedule_verify_lens_topk_from_survival(
survival_probs=compute_sort_survival(confidence), budget=budget, cfg=cfg
)
def schedule_verify_lens_topk_from_survival(
*,
survival_probs: torch.Tensor,
budget: int,
cfg: DSparkScheduleConfig,
) -> torch.Tensor:
num_requests, _gamma = survival_probs.shape
max_len = cfg.resolved_max_verify_len()
device = survival_probs.device
selected_extra = torch.zeros(num_requests, dtype=torch.int64, device=device)
if budget > 0:
candidate_window = survival_probs[:, :max_len]
num_candidates = candidate_window.numel()
if num_candidates > 0:
request_index = (
torch.arange(num_requests, device=device)
.view(num_requests, 1)
.expand_as(candidate_window)
)
position_index = (
torch.arange(candidate_window.shape[1], device=device)
.view(1, candidate_window.shape[1])
.expand_as(candidate_window)
)
valid = candidate_window >= cfg.survival_eps
flat_prob = candidate_window.reshape(-1).to(torch.float64)
flat_request = request_index.reshape(-1)
flat_position = position_index.reshape(-1)
flat_valid = valid.reshape(-1)
order = _value_independent_descending_order(
probs=flat_prob,
positions=flat_position,
requests=flat_request,
valid=flat_valid,
)
take = min(int(budget), num_candidates)
chosen = order[:take]
chosen_requests = flat_request[chosen]
chosen_valid = flat_valid[chosen].to(torch.int64)
selected_extra.scatter_add_(0, chosen_requests, chosen_valid)
min_len = torch.full(
(num_requests,), cfg.min_verify_len, dtype=torch.int64, device=device
)
verify_lens = min_len + selected_extra
lower_bound = max(cfg.min_verify_len, 1)
verify_lens = torch.clamp(verify_lens, min=lower_bound, max=max_len)
return verify_lens.to(torch.int32)
def _value_independent_descending_order(
*,
probs: torch.Tensor,
positions: torch.Tensor,
requests: torch.Tensor,
valid: torch.Tensor,
) -> torch.Tensor:
masked_prob = torch.where(valid, probs, torch.full_like(probs, float("-inf")))
num_candidates = masked_prob.numel()
order = torch.arange(num_candidates, device=probs.device)
order = order[torch.argsort(requests[order], stable=True)]
order = order[torch.argsort(positions[order], stable=True)]
order = order[torch.argsort(-masked_prob[order], stable=True)]
return order
@triton.jit
def _schedule_topk_prep_kernel(
confidence_ptr,
survival_ptr,
selected_extra_ptr,
gamma,
cols,
G_P2: tl.constexpr,
):
row = tl.program_id(0)
g = tl.arange(0, G_P2)
conf = tl.load(
confidence_ptr + row.to(tl.int64) * gamma + g, mask=g < gamma, other=1.0
).to(tl.float32)
surv = tl.cumprod(conf, axis=0)
tl.store(survival_ptr + row.to(tl.int64) * cols + g, surv, mask=g < cols)
tl.store(selected_extra_ptr + row, 0)
@triton.jit
def _schedule_topk_finalize_kernel(
selected_extra_ptr,
out_ptr,
min_verify_len,
lower_bound,
max_len,
bs,
BLOCK: tl.constexpr,
):
offs = tl.program_id(0) * BLOCK + tl.arange(0, BLOCK)
mask = offs < bs
extra = tl.load(selected_extra_ptr + offs, mask=mask, other=0).to(tl.int32)
lens = min_verify_len + extra
lens = tl.maximum(lens, lower_bound)
lens = tl.minimum(lens, max_len)
tl.store(out_ptr + offs, lens, mask=mask)
@triton.jit
def _schedule_topk_selected_extra_kernel(
survival_ptr,
selected_extra_ptr,
budget,
cols,
n,
survival_eps,
BLOCK_C: tl.constexpr,
BLOCK_CP: tl.constexpr,
):
pid = tl.program_id(0)
c = pid * BLOCK_C + tl.arange(0, BLOCK_C)
cmask = c < n
r = c // cols
p = c % cols
sp = tl.load(survival_ptr + c, mask=cmask, other=0.0)
valid_c = sp >= survival_eps
mp = tl.where(valid_c, sp, float("-inf"))
rank = tl.zeros([BLOCK_C], dtype=tl.int32)
for cp0 in range(0, n, BLOCK_CP):
cp = cp0 + tl.arange(0, BLOCK_CP)
cpmask = cp < n
rp = cp // cols
pp = cp % cols
spp = tl.load(survival_ptr + cp, mask=cpmask, other=0.0)
validp = spp >= survival_eps
mpp = tl.where(validp, spp, float("-inf"))
gt = mpp[None, :] > mp[:, None]
eq = mpp[None, :] == mp[:, None]
pos_lt = pp[None, :] < p[:, None]
pos_eq = pp[None, :] == p[:, None]
req_lt = rp[None, :] < r[:, None]
before = gt | (eq & (pos_lt | (pos_eq & req_lt)))
before = before & cpmask[None, :]
rank += tl.sum(before.to(tl.int32), axis=1)
selected = valid_c & (rank < budget)
tl.atomic_add(selected_extra_ptr + r, selected.to(tl.int32), mask=cmask)
def schedule_verify_lens_topk_triton(
*,
confidence: torch.Tensor,
budget: int,
cfg: DSparkScheduleConfig,
) -> torch.Tensor:
num_requests, gamma = confidence.shape
max_len = cfg.resolved_max_verify_len()
device = confidence.device
cols = min(max_len, gamma)
n = num_requests * cols
selected_extra = torch.empty(num_requests, dtype=torch.int32, device=device)
survival = torch.empty((num_requests, cols), dtype=torch.float32, device=device)
_schedule_topk_prep_kernel[(num_requests,)](
confidence.contiguous(),
survival,
selected_extra,
gamma,
cols,
G_P2=triton.next_power_of_2(max(gamma, 1)),
)
if budget > 0 and n > 0:
BLOCK_C = 64
BLOCK_CP = 256
grid = (triton.cdiv(n, BLOCK_C),)
_schedule_topk_selected_extra_kernel[grid](
survival,
selected_extra,
int(budget),
cols,
n,
float(cfg.survival_eps),
BLOCK_C=BLOCK_C,
BLOCK_CP=BLOCK_CP,
)
verify_lens = torch.empty(num_requests, dtype=torch.int32, device=device)
BLOCK = 256
_schedule_topk_finalize_kernel[(triton.cdiv(num_requests, BLOCK),)](
selected_extra,
verify_lens,
int(cfg.min_verify_len),
max(cfg.min_verify_len, 1),
int(max_len),
num_requests,
BLOCK=BLOCK,
)
return verify_lens
@@ -0,0 +1,871 @@
from __future__ import annotations
import msgspec
import torch
import triton
import triton.language as tl
from sglang.kernels.ops.speculative.cache_locs import assign_extend_cache_locs_func
from sglang.srt.managers.schedule_batch import ScheduleBatch
from sglang.srt.speculative.dspark_components.kernels.dispatch import inputs_on_cuda
from sglang.srt.speculative.ragged_verify import RaggedVerifyLayout
class RaggedVerifyWindow(msgspec.Struct, frozen=True):
positions: torch.Tensor
verify_cache_loc: torch.Tensor
verify_ids: torch.Tensor
class BuildRaggedVerifyWindow:
@classmethod
def execute(cls, *args, **kwargs) -> RaggedVerifyWindow:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
batch: ScheduleBatch,
layout: RaggedVerifyLayout,
draft_block_ids: torch.Tensor,
draft_tokens: torch.Tensor,
bs: int,
device: str,
verify_num_draft_tokens: int,
model_runner,
) -> RaggedVerifyWindow:
return build_ragged_verify_window(
batch=batch,
layout=layout,
draft_block_ids=draft_block_ids,
draft_tokens=draft_tokens,
bs=bs,
device=device,
verify_num_draft_tokens=verify_num_draft_tokens,
model_runner=model_runner,
)
@classmethod
def triton(
cls,
*,
batch: ScheduleBatch,
layout: RaggedVerifyLayout,
draft_block_ids: torch.Tensor,
draft_tokens: torch.Tensor,
bs: int,
device: str,
verify_num_draft_tokens: int,
model_runner,
) -> RaggedVerifyWindow:
return build_ragged_verify_window_triton(
batch=batch,
layout=layout,
draft_block_ids=draft_block_ids,
draft_tokens=draft_tokens,
bs=bs,
device=device,
verify_num_draft_tokens=verify_num_draft_tokens,
model_runner=model_runner,
)
def build_ragged_verify_window(
*,
batch: ScheduleBatch,
layout: RaggedVerifyLayout,
draft_block_ids: torch.Tensor,
draft_tokens: torch.Tensor,
bs: int,
device: str,
verify_num_draft_tokens: int,
model_runner,
) -> RaggedVerifyWindow:
prefix_lens = batch.seq_lens
verify_lens = layout.verify_lens.to(device=device, dtype=torch.int32)
padded_total = layout.graph_num_tokens
req_id, within, valid = compact_row_index(
verify_lens=verify_lens, padded_total=padded_total, device=device
)
safe_req = req_id.clamp(max=bs - 1)
positions = torch.where(
valid,
prefix_lens.to(torch.int64)[safe_req] + within,
torch.zeros_like(within),
)
real_cache_loc = assign_extend_cache_locs_func(
req_pool_indices=batch.req_pool_indices,
req_to_token=model_runner.req_to_token_pool.req_to_token,
start_offset=prefix_lens,
end_offset=prefix_lens + verify_lens.to(prefix_lens.dtype),
batch_size=bs,
draft_token_num=verify_num_draft_tokens,
device=device,
)
verify_cache_loc = torch.nn.functional.pad(
real_cache_loc, (0, padded_total - real_cache_loc.shape[0])
)
verify_cache_loc = torch.where(
valid, verify_cache_loc, torch.zeros_like(verify_cache_loc)
)
verify_ids = compact_verify_ids(
draft_block_ids=draft_block_ids,
draft_tokens=draft_tokens,
layout=layout,
device=device,
)
return RaggedVerifyWindow(
positions=positions,
verify_cache_loc=verify_cache_loc,
verify_ids=verify_ids,
)
@triton.jit
def _ragged_finalize_kernel(
req_ptr,
within_ptr,
prefix_ptr,
cache_ptr,
pos_out_ptr,
cache_out_ptr,
bs,
n,
real_len,
BLOCK: tl.constexpr,
):
pid = tl.program_id(0)
offs = pid * BLOCK + tl.arange(0, BLOCK)
mask = offs < n
req = tl.load(req_ptr + offs, mask=mask, other=0)
within = tl.load(within_ptr + offs, mask=mask, other=0)
valid = req < bs
safe_req = tl.minimum(req, bs - 1)
prefix = tl.load(prefix_ptr + safe_req, mask=mask, other=0)
pos = tl.where(valid, prefix + within, 0)
lmask = mask & (offs < real_len)
cl = tl.load(cache_ptr + offs, mask=lmask, other=0)
cl = tl.where(valid, cl, 0)
tl.store(pos_out_ptr + offs, pos, mask=mask)
tl.store(cache_out_ptr + offs, cl, mask=mask)
def build_ragged_verify_window_triton(
*,
batch: ScheduleBatch,
layout: RaggedVerifyLayout,
draft_block_ids: torch.Tensor,
draft_tokens: torch.Tensor,
bs: int,
device: str,
verify_num_draft_tokens: int,
model_runner,
) -> RaggedVerifyWindow:
prefix_lens = batch.seq_lens
verify_lens = layout.verify_lens.to(device=device, dtype=torch.int32)
padded_total = layout.graph_num_tokens
req_id, within, _valid = compact_row_index_triton(
verify_lens=verify_lens, padded_total=padded_total, device=device
)
real_cache_loc = assign_extend_cache_locs_func(
req_pool_indices=batch.req_pool_indices,
req_to_token=model_runner.req_to_token_pool.req_to_token,
start_offset=prefix_lens,
end_offset=prefix_lens + verify_lens.to(prefix_lens.dtype),
batch_size=bs,
draft_token_num=verify_num_draft_tokens,
device=device,
)
prefix_i64 = prefix_lens.to(device=device, dtype=torch.int64).contiguous()
positions = torch.empty(padded_total, dtype=torch.int64, device=device)
verify_cache_loc = torch.empty(
padded_total, dtype=real_cache_loc.dtype, device=device
)
BLOCK = 256
grid = (triton.cdiv(padded_total, BLOCK),)
_ragged_finalize_kernel[grid](
req_id,
within,
prefix_i64,
real_cache_loc,
positions,
verify_cache_loc,
bs,
padded_total,
real_cache_loc.shape[0],
BLOCK=BLOCK,
)
verify_ids = compact_verify_ids_triton(
draft_block_ids=draft_block_ids,
draft_tokens=draft_tokens,
layout=layout,
device=device,
)
return RaggedVerifyWindow(
positions=positions,
verify_cache_loc=verify_cache_loc,
verify_ids=verify_ids,
)
_SEARCH_NBITS = 11
class CompactRowIndex:
@classmethod
def execute(
cls, *args, **kwargs
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
verify_lens: torch.Tensor,
padded_total: int,
device,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
return compact_row_index(
verify_lens=verify_lens,
padded_total=padded_total,
device=device,
)
@classmethod
def triton(
cls,
*,
verify_lens: torch.Tensor,
padded_total: int,
device,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
return compact_row_index_triton(
verify_lens=verify_lens,
padded_total=padded_total,
device=device,
)
class CompactVerifyIds:
@classmethod
def execute(cls, *args, **kwargs) -> torch.Tensor:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
draft_block_ids: torch.Tensor,
draft_tokens: torch.Tensor,
layout: RaggedVerifyLayout,
device: str,
) -> torch.Tensor:
return compact_verify_ids(
draft_block_ids=draft_block_ids,
draft_tokens=draft_tokens,
layout=layout,
device=device,
)
@classmethod
def triton(
cls,
*,
draft_block_ids: torch.Tensor,
draft_tokens: torch.Tensor,
layout: RaggedVerifyLayout,
device: str,
) -> torch.Tensor:
return compact_verify_ids_triton(
draft_block_ids=draft_block_ids,
draft_tokens=draft_tokens,
layout=layout,
device=device,
)
def compact_verify_ids(
*,
draft_block_ids: torch.Tensor,
draft_tokens: torch.Tensor,
layout: RaggedVerifyLayout,
device: str,
) -> torch.Tensor:
req_id, within, valid = compact_row_index(
verify_lens=layout.verify_lens,
padded_total=layout.graph_num_tokens,
device=device,
)
bs = layout.verify_lens.shape[0]
safe_req = req_id.clamp(max=bs - 1)
anchors = draft_block_ids[:, 0]
drafts = draft_tokens[safe_req, (within - 1).clamp_min(0)]
verify_ids = torch.where(within == 0, anchors[safe_req], drafts)
verify_ids = torch.where(valid, verify_ids, torch.zeros_like(verify_ids))
return verify_ids.to(torch.int64)
def compact_row_index(
*,
verify_lens: torch.Tensor,
padded_total: int,
device,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
verify_lens = verify_lens.to(device=device, dtype=torch.int64)
bs = int(verify_lens.numel())
incl = torch.cumsum(verify_lens, dim=0)
start = incl - verify_lens
real_total = incl[-1]
row = torch.arange(padded_total, device=device, dtype=torch.int64)
valid = row < real_total
req_id = torch.searchsorted(incl, row, right=True)
req_id = torch.where(valid, req_id, torch.full_like(req_id, bs))
within = torch.where(
valid, row - start[req_id.clamp(max=bs - 1)], torch.zeros_like(row)
)
return req_id, within, valid
@triton.jit
def _compact_row_index_kernel(
incl_ptr,
req_out_ptr,
within_out_ptr,
valid_out_ptr,
bs,
n,
BLOCK: tl.constexpr,
NBITS: tl.constexpr,
):
pid = tl.program_id(0)
offs = pid * BLOCK + tl.arange(0, BLOCK)
mask = offs < n
row = offs.to(tl.int64)
real_total = tl.load(incl_ptr + (bs - 1))
lo = tl.zeros([BLOCK], dtype=tl.int32)
hi = tl.full([BLOCK], bs, dtype=tl.int32)
for _ in range(NBITS):
mid = (lo + hi) // 2
active = lo < hi
val = tl.load(incl_ptr + tl.minimum(mid, bs - 1), mask=mask, other=0)
go_right = val <= row
lo = tl.where(active & go_right, mid + 1, lo)
hi = tl.where(active & (~go_right), mid, hi)
req = lo
gidx = tl.maximum(req - 1, 0)
start = tl.load(incl_ptr + gidx, mask=mask, other=0)
start = tl.where(req > 0, start, 0)
valid = row < real_total
within = tl.where(valid, row - start, 0)
req_final = tl.where(valid, req.to(tl.int64), bs)
tl.store(req_out_ptr + offs, req_final, mask=mask)
tl.store(within_out_ptr + offs, within, mask=mask)
tl.store(valid_out_ptr + offs, valid, mask=mask)
def compact_row_index_triton(
*,
verify_lens: torch.Tensor,
padded_total: int,
device,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
verify_lens = verify_lens.to(device=device, dtype=torch.int64).contiguous()
bs = verify_lens.shape[0]
incl = torch.cumsum(verify_lens, dim=0).contiguous()
req = torch.empty(padded_total, dtype=torch.int64, device=device)
within = torch.empty(padded_total, dtype=torch.int64, device=device)
valid = torch.empty(padded_total, dtype=torch.bool, device=device)
BLOCK = 256
grid = (triton.cdiv(padded_total, BLOCK),)
_compact_row_index_kernel[grid](
incl, req, within, valid, bs, padded_total, BLOCK=BLOCK, NBITS=_SEARCH_NBITS
)
return req, within, valid
@triton.jit
def _compact_verify_ids_gather_kernel(
req_ptr,
within_ptr,
draft_block_ids_ptr,
draft_tokens_ptr,
out_ptr,
bs,
gamma,
n,
BLOCK: tl.constexpr,
):
pid = tl.program_id(0)
offs = pid * BLOCK + tl.arange(0, BLOCK)
mask = offs < n
req = tl.load(req_ptr + offs, mask=mask, other=0)
within = tl.load(within_ptr + offs, mask=mask, other=0)
valid = req < bs
safe_req = tl.minimum(req, bs - 1)
anchor = tl.load(draft_block_ids_ptr + safe_req * gamma, mask=mask, other=0)
wcol = tl.maximum(within - 1, 0)
draft = tl.load(draft_tokens_ptr + safe_req * gamma + wcol, mask=mask, other=0)
v = tl.where(within == 0, anchor, draft)
v = tl.where(valid, v, 0)
tl.store(out_ptr + offs, v.to(tl.int64), mask=mask)
def compact_verify_ids_triton(
*,
draft_block_ids: torch.Tensor,
draft_tokens: torch.Tensor,
layout: RaggedVerifyLayout,
device: str,
) -> torch.Tensor:
req, within, _valid = compact_row_index_triton(
verify_lens=layout.verify_lens,
padded_total=layout.graph_num_tokens,
device=device,
)
bs = layout.verify_lens.shape[0]
gamma = draft_tokens.shape[1]
draft_block_ids = draft_block_ids.to(device=device, dtype=torch.int64).contiguous()
draft_tokens = draft_tokens.to(device=device, dtype=torch.int64).contiguous()
n = layout.graph_num_tokens
out = torch.empty(n, dtype=torch.int64, device=device)
BLOCK = 256
grid = (triton.cdiv(n, BLOCK),)
_compact_verify_ids_gather_kernel[grid](
req, within, draft_block_ids, draft_tokens, out, bs, gamma, n, BLOCK=BLOCK
)
return out
class ScatterCompactToStrided:
@classmethod
def execute(cls, *args, **kwargs) -> torch.Tensor:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
compact: torch.Tensor,
layout: RaggedVerifyLayout,
fill_value: float,
verify_num_draft_tokens: int,
) -> torch.Tensor:
return scatter_compact_to_strided(
compact=compact,
layout=layout,
fill_value=fill_value,
verify_num_draft_tokens=verify_num_draft_tokens,
)
@classmethod
def triton(
cls,
*,
compact: torch.Tensor,
layout: RaggedVerifyLayout,
fill_value: float,
verify_num_draft_tokens: int,
) -> torch.Tensor:
return scatter_compact_to_strided_triton(
compact=compact,
layout=layout,
fill_value=fill_value,
verify_num_draft_tokens=verify_num_draft_tokens,
)
def scatter_compact_to_strided(
*,
compact: torch.Tensor,
layout: RaggedVerifyLayout,
fill_value: float,
verify_num_draft_tokens: int,
) -> torch.Tensor:
stride = verify_num_draft_tokens
bs = layout.verify_lens.shape[0]
dim = compact.shape[1]
device = compact.device
compact = compact[: layout.graph_num_tokens]
strided = torch.full(
(bs * stride + 1, dim), fill_value, dtype=compact.dtype, device=device
)
req_id, within, valid = compact_row_index(
verify_lens=layout.verify_lens,
padded_total=layout.graph_num_tokens,
device=device,
)
sink = bs * stride
strided_pos = torch.where(
valid,
req_id.clamp(max=bs - 1) * stride + within,
torch.full_like(within, sink),
)
strided.index_copy_(0, strided_pos, compact)
return strided[: bs * stride]
@triton.jit
def _scatter_compact_to_strided_kernel(
compact_ptr,
verify_lens_ptr,
start_ptr,
out_ptr,
stride,
dim,
fill_value,
BLOCK_D: tl.constexpr,
):
o = tl.program_id(0).to(tl.int64)
dblk = tl.program_id(1)
i = o // stride
w = o % stride
vl_i = tl.load(verify_lens_ptr + i)
start_i = tl.load(start_ptr + i)
d = dblk * BLOCK_D + tl.arange(0, BLOCK_D)
dmask = d < dim
in_range = w < vl_i
src = tl.where(in_range, start_i + w, 0)
val = tl.load(compact_ptr + src * dim + d, mask=dmask & in_range, other=0)
val = tl.where(in_range, val, fill_value)
tl.store(out_ptr + o * dim + d, val, mask=dmask)
def scatter_compact_to_strided_into(
*,
compact: torch.Tensor,
verify_lens: torch.Tensor,
out: torch.Tensor,
stride: int,
fill_value: float,
) -> torch.Tensor:
dim = compact.shape[1]
fill_value = float(fill_value) if out.dtype.is_floating_point else int(fill_value)
compact = compact.contiguous()
verify_lens = verify_lens.to(dtype=torch.int64).contiguous()
start = (torch.cumsum(verify_lens, dim=0) - verify_lens).contiguous()
n_out = out.shape[0]
BLOCK_D = 1024
grid = (n_out, triton.cdiv(dim, BLOCK_D))
_scatter_compact_to_strided_kernel[grid](
compact,
verify_lens,
start,
out,
stride,
dim,
fill_value,
BLOCK_D=BLOCK_D,
)
return out
def scatter_compact_to_strided_triton(
*,
compact: torch.Tensor,
layout: RaggedVerifyLayout,
fill_value: float,
verify_num_draft_tokens: int,
) -> torch.Tensor:
stride = verify_num_draft_tokens
bs = layout.verify_lens.shape[0]
dim = compact.shape[1]
device = compact.device
out = torch.empty((bs * stride, dim), dtype=compact.dtype, device=device)
return scatter_compact_to_strided_into(
compact=compact,
verify_lens=layout.verify_lens.to(device=device),
out=out,
stride=stride,
fill_value=fill_value,
)
class CommitInjectLayoutResult(msgspec.Struct):
swa_loc: torch.Tensor
positions: torch.Tensor
class BuildCommitInjectLayout:
@classmethod
def execute(cls, *args, **kwargs) -> CommitInjectLayoutResult:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
req_pool_indices: torch.Tensor,
req_to_token: torch.Tensor,
prefix_lens: torch.Tensor,
block_pos_offsets: torch.Tensor,
full_to_swa_mapping: torch.Tensor,
commit_lens: torch.Tensor,
stride: int,
) -> CommitInjectLayoutResult:
return build_commit_inject_layout(
req_pool_indices=req_pool_indices,
req_to_token=req_to_token,
prefix_lens=prefix_lens,
block_pos_offsets=block_pos_offsets,
full_to_swa_mapping=full_to_swa_mapping,
commit_lens=commit_lens,
stride=stride,
)
@classmethod
def triton(
cls,
*,
req_pool_indices: torch.Tensor,
req_to_token: torch.Tensor,
prefix_lens: torch.Tensor,
block_pos_offsets: torch.Tensor,
full_to_swa_mapping: torch.Tensor,
commit_lens: torch.Tensor,
stride: int,
) -> CommitInjectLayoutResult:
return build_commit_inject_layout_triton(
req_pool_indices=req_pool_indices,
req_to_token=req_to_token,
prefix_lens=prefix_lens,
block_pos_offsets=block_pos_offsets,
full_to_swa_mapping=full_to_swa_mapping,
commit_lens=commit_lens,
stride=stride,
)
def build_commit_inject_layout(
*,
req_pool_indices: torch.Tensor,
req_to_token: torch.Tensor,
prefix_lens: torch.Tensor,
block_pos_offsets: torch.Tensor,
full_to_swa_mapping: torch.Tensor,
commit_lens: torch.Tensor,
stride: int,
) -> CommitInjectLayoutResult:
from sglang.kernels.ops.speculative.cache_locs import (
assign_extend_cache_locs_func,
)
bs = req_pool_indices.shape[0]
device = req_pool_indices.device
positions_2d = prefix_lens.unsqueeze(1) + block_pos_offsets[:stride]
positions = positions_2d.reshape(-1).to(dtype=torch.int64)
cache_loc = assign_extend_cache_locs_func(
req_pool_indices=req_pool_indices,
req_to_token=req_to_token,
start_offset=prefix_lens,
end_offset=prefix_lens + stride,
batch_size=bs,
draft_token_num=stride,
device=device,
).to(dtype=torch.int64)
swa_loc = full_to_swa_mapping[cache_loc].to(torch.int32)
col = torch.arange(stride, device=device).view(1, -1)
committed = (col < commit_lens.to(torch.long).view(-1, 1)).reshape(-1)
swa_loc = torch.where(committed, swa_loc, torch.full_like(swa_loc, -1))
return CommitInjectLayoutResult(swa_loc=swa_loc, positions=positions)
@triton.jit
def _commit_inject_layout_kernel(
req_pool_ptr,
req_to_token_ptr,
prefix_lens_ptr,
block_pos_offsets_ptr,
full_to_swa_ptr,
commit_lens_ptr,
swa_loc_ptr,
positions_ptr,
rt_stride,
stride,
n,
BLOCK: tl.constexpr,
):
offs = tl.program_id(0) * BLOCK + tl.arange(0, BLOCK)
mask = offs < n
r = offs // stride
c = offs % stride
prefix = tl.load(prefix_lens_ptr + r, mask=mask, other=0).to(tl.int64)
pos_off = tl.load(block_pos_offsets_ptr + c, mask=mask, other=0).to(tl.int64)
rp = tl.load(req_pool_ptr + r, mask=mask, other=0).to(tl.int64)
full_loc = tl.load(
req_to_token_ptr + rp * rt_stride + prefix + pos_off, mask=mask, other=0
).to(tl.int64)
swa = tl.load(full_to_swa_ptr + full_loc, mask=mask, other=-1).to(tl.int32)
commit_len = tl.load(commit_lens_ptr + r, mask=mask, other=0).to(tl.int64)
swa = tl.where(c.to(tl.int64) < commit_len, swa, -1)
tl.store(swa_loc_ptr + offs, swa, mask=mask)
tl.store(positions_ptr + offs, prefix + pos_off, mask=mask)
def build_commit_inject_layout_triton(
*,
req_pool_indices: torch.Tensor,
req_to_token: torch.Tensor,
prefix_lens: torch.Tensor,
block_pos_offsets: torch.Tensor,
full_to_swa_mapping: torch.Tensor,
commit_lens: torch.Tensor,
stride: int,
) -> CommitInjectLayoutResult:
bs = req_pool_indices.shape[0]
n = bs * stride
device = req_pool_indices.device
swa_loc = torch.empty(n, dtype=torch.int32, device=device)
positions = torch.empty(n, dtype=torch.int64, device=device)
BLOCK = 256
_commit_inject_layout_kernel[(triton.cdiv(n, BLOCK),)](
req_pool_indices,
req_to_token,
prefix_lens,
block_pos_offsets,
full_to_swa_mapping,
commit_lens,
swa_loc,
positions,
req_to_token.stride(0),
stride,
n,
BLOCK=BLOCK,
)
return CommitInjectLayoutResult(swa_loc=swa_loc, positions=positions)
class BuildOutTokens:
@classmethod
def execute(cls, *args, **kwargs) -> torch.Tensor:
if inputs_on_cuda(*args, **kwargs):
return cls.triton(*args, **kwargs)
return cls.torch(*args, **kwargs)
@classmethod
def torch(
cls,
*,
draft_tokens: torch.Tensor,
correct_len: torch.Tensor,
bonus: torch.Tensor,
verify_num_draft_tokens: int,
gamma: int,
) -> torch.Tensor:
return build_out_tokens(
draft_tokens=draft_tokens,
correct_len=correct_len,
bonus=bonus,
verify_num_draft_tokens=verify_num_draft_tokens,
gamma=gamma,
)
@classmethod
def triton(
cls,
*,
draft_tokens: torch.Tensor,
correct_len: torch.Tensor,
bonus: torch.Tensor,
verify_num_draft_tokens: int,
gamma: int,
) -> torch.Tensor:
return build_out_tokens_triton(
draft_tokens=draft_tokens,
correct_len=correct_len,
bonus=bonus,
verify_num_draft_tokens=verify_num_draft_tokens,
gamma=gamma,
)
def build_out_tokens(
*,
draft_tokens: torch.Tensor,
correct_len: torch.Tensor,
bonus: torch.Tensor,
verify_num_draft_tokens: int,
gamma: int,
) -> torch.Tensor:
bs = draft_tokens.shape[0]
out_tokens = torch.empty(
(bs, verify_num_draft_tokens),
dtype=torch.int64,
device=draft_tokens.device,
)
out_tokens[:, :gamma].copy_(draft_tokens)
out_tokens[:, gamma].fill_(0)
out_tokens.scatter_(1, correct_len.to(torch.int64)[:, None], bonus[:, None])
return out_tokens
@triton.jit
def _build_out_tokens_kernel(
draft_tokens_ptr,
correct_len_ptr,
bonus_ptr,
out_ptr,
gamma,
T,
n_out,
BLOCK: tl.constexpr,
):
pid = tl.program_id(0)
offs = pid * BLOCK + tl.arange(0, BLOCK)
mask = offs < n_out
b = offs // T
k = offs % T
cl = tl.load(correct_len_ptr + b, mask=mask, other=0).to(tl.int32)
bonus = tl.load(bonus_ptr + b, mask=mask, other=0)
draft_mask = mask & (k < gamma)
draft = tl.load(draft_tokens_ptr + b * gamma + k, mask=draft_mask, other=0)
val = tl.where(k == cl, bonus, tl.where(k < gamma, draft, 0))
tl.store(out_ptr + offs, val.to(tl.int64), mask=mask)
def build_out_tokens_triton(
*,
draft_tokens: torch.Tensor,
correct_len: torch.Tensor,
bonus: torch.Tensor,
verify_num_draft_tokens: int,
gamma: int,
) -> torch.Tensor:
bs = draft_tokens.shape[0]
T = verify_num_draft_tokens
device = draft_tokens.device
draft_tokens = draft_tokens.to(torch.int64).contiguous()
correct_len_i = correct_len.to(torch.int64).contiguous()
bonus_i = bonus.to(torch.int64).contiguous()
out = torch.empty((bs, T), dtype=torch.int64, device=device)
n_out = bs * T
BLOCK = 256
grid = (triton.cdiv(n_out, BLOCK),)
_build_out_tokens_kernel[grid](
draft_tokens, correct_len_i, bonus_i, out, gamma, T, n_out, BLOCK=BLOCK
)
return out