94057c3d3e
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
258 lines
8.6 KiB
Python
258 lines
8.6 KiB
Python
from typing import Optional
|
|
|
|
import torch
|
|
|
|
from sglang.srt.layers.attention.linear.kernels.kernel_backend import (
|
|
LinearAttnKernelBase,
|
|
)
|
|
|
|
# FlashKDA chunk size. Sequences shorter than this fall back to Triton.
|
|
_FLASHKDA_CHUNK_SIZE = 64
|
|
|
|
# FlashKDA's max sequence length, Batches whose longest sequence exceeds this
|
|
# fall back to Triton for the whole batch.
|
|
_FLASHKDA_MAX_SEQ_LEN = 2048
|
|
|
|
|
|
def _load_flash_kda():
|
|
"""Import the optional ``flash_kda`` CUTLASS module."""
|
|
try:
|
|
import flash_kda
|
|
except ImportError as e:
|
|
raise ImportError(
|
|
"The 'flashkda' KDA prefill backend requires the flash_kda module, "
|
|
"which is not installed. Install it from source:\n"
|
|
" pip install git+https://github.com/MoonshotAI/FlashKDA.git"
|
|
) from e
|
|
return flash_kda
|
|
|
|
|
|
def _triton_fallback(
|
|
q,
|
|
k,
|
|
v,
|
|
g,
|
|
beta,
|
|
ssm_states,
|
|
cache_indices,
|
|
query_start_loc,
|
|
A_log=None,
|
|
dt_bias=None,
|
|
lower_bound=None,
|
|
):
|
|
"""Fall back to the Triton chunk_kda kernel (handles all preprocessing).
|
|
|
|
`g` is the RAW gate; chunk_kda applies the gate activation internally when
|
|
A_log is provided, so A_log/dt_bias/lower_bound must be threaded through too
|
|
-- otherwise the fallback silently skips activation. chunk_kda updates the
|
|
ssm state in-place via cache_indices and returns only the output tensor.
|
|
"""
|
|
from sglang.srt.layers.attention.fla.kda import chunk_kda
|
|
|
|
return chunk_kda(
|
|
q=q,
|
|
k=k,
|
|
v=v,
|
|
g=g,
|
|
beta=beta,
|
|
initial_state=ssm_states,
|
|
initial_state_indices=cache_indices,
|
|
use_qk_l2norm_in_kernel=True,
|
|
cu_seqlens=query_start_loc,
|
|
A_log=A_log,
|
|
dt_bias=dt_bias,
|
|
lower_bound=lower_bound,
|
|
)
|
|
|
|
|
|
class FlashKDAKernel(LinearAttnKernelBase):
|
|
"""FlashKDA (MoonshotAI) fully-fused CUTLASS KDA prefill backend.
|
|
|
|
Wraps the external ``flash_kda`` package (https://github.com/MoonshotAI/FlashKDA).
|
|
|
|
FlashKDA fuses q/k L2 norm, beta sigmoid, and the KDA gate *inside* the
|
|
kernel, so we pass RAW tensors plus ``A_log``/``dt_bias``/``lower_bound``.
|
|
It is prefill-only, bf16, K == V == 128, HV == H (no GVA), and requires the
|
|
safe (bounded) gate (``lower_bound`` set). The non-safe path and sequences
|
|
outside [chunk_size, max_seq_len] fall back to Triton ``chunk_kda``.
|
|
Requires an SM90+ GPU with the ``flash_kda`` package.
|
|
"""
|
|
|
|
def decode(
|
|
self,
|
|
q: torch.Tensor,
|
|
k: torch.Tensor,
|
|
v: torch.Tensor,
|
|
a: torch.Tensor,
|
|
b: torch.Tensor,
|
|
*,
|
|
A_log: torch.Tensor,
|
|
dt_bias: torch.Tensor,
|
|
ssm_states: torch.Tensor,
|
|
cache_indices: torch.Tensor,
|
|
query_start_loc: torch.Tensor,
|
|
**kwargs,
|
|
) -> torch.Tensor:
|
|
raise NotImplementedError("FlashKDAKernel only supports prefill (extend)")
|
|
|
|
def extend(
|
|
self,
|
|
q: torch.Tensor,
|
|
k: torch.Tensor,
|
|
v: torch.Tensor,
|
|
g: torch.Tensor,
|
|
beta: torch.Tensor,
|
|
*,
|
|
ssm_states: torch.Tensor,
|
|
cache_indices: torch.Tensor,
|
|
query_start_loc: torch.Tensor,
|
|
A_log: Optional[torch.Tensor] = None,
|
|
dt_bias: Optional[torch.Tensor] = None,
|
|
lower_bound: Optional[float] = None,
|
|
extend_seq_lens_cpu: Optional[list] = None,
|
|
is_spec_decode: bool = False,
|
|
**kwargs,
|
|
) -> torch.Tensor:
|
|
if self._should_fall_back(
|
|
lower_bound, is_spec_decode, query_start_loc, extend_seq_lens_cpu
|
|
):
|
|
return _triton_fallback(
|
|
q,
|
|
k,
|
|
v,
|
|
g,
|
|
beta,
|
|
ssm_states,
|
|
cache_indices,
|
|
query_start_loc,
|
|
A_log=A_log,
|
|
dt_bias=dt_bias,
|
|
lower_bound=lower_bound,
|
|
)
|
|
|
|
return self._flashkda_extend(
|
|
q,
|
|
k,
|
|
v,
|
|
g,
|
|
beta,
|
|
ssm_states=ssm_states,
|
|
cache_indices=cache_indices,
|
|
query_start_loc=query_start_loc,
|
|
A_log=A_log,
|
|
dt_bias=dt_bias,
|
|
lower_bound=lower_bound,
|
|
)
|
|
|
|
@staticmethod
|
|
def _should_fall_back(
|
|
lower_bound: Optional[float],
|
|
is_spec_decode: bool,
|
|
query_start_loc: torch.Tensor,
|
|
extend_seq_lens_cpu: Optional[list],
|
|
) -> bool:
|
|
"""Whether to use the Triton chunk_kda path instead of the fused kernel."""
|
|
# Safe-gate only: the fused kernel does not support the unbounded gate
|
|
# (-exp(A_log)*softplus); those models leave lower_bound unset.
|
|
if lower_bound is None:
|
|
return True
|
|
# FlashKDA writes the committed recurrent state back in place, so it is
|
|
# unsafe for speculative verify / draft-extend forwards (which must stay
|
|
# rollback-able). Those reach this backend through forward_extend, so
|
|
# gate them here rather than relying on the decode/target_verify stubs.
|
|
if is_spec_decode:
|
|
return True
|
|
# Short sequences (< chunk size) and long sequences (> the crossover
|
|
# where Triton's chunked prefill wins) are faster on Triton. Read the
|
|
# per-request lengths from the CPU-side extend_seq_lens to avoid a
|
|
# GPU->CPU sync on every layer; derive from query_start_loc (one sync)
|
|
# only if they are unavailable.
|
|
if extend_seq_lens_cpu is not None:
|
|
if torch.is_tensor(extend_seq_lens_cpu):
|
|
lo = int(extend_seq_lens_cpu.min())
|
|
hi = int(extend_seq_lens_cpu.max())
|
|
else:
|
|
lo = min(extend_seq_lens_cpu)
|
|
hi = max(extend_seq_lens_cpu)
|
|
else:
|
|
seq_lens = query_start_loc[1:] - query_start_loc[:-1]
|
|
lo_t, hi_t = torch.aminmax(seq_lens)
|
|
lo, hi = int(lo_t), int(hi_t)
|
|
return lo < _FLASHKDA_CHUNK_SIZE or hi > _FLASHKDA_MAX_SEQ_LEN
|
|
|
|
def _flashkda_extend(
|
|
self,
|
|
q: torch.Tensor,
|
|
k: torch.Tensor,
|
|
v: torch.Tensor,
|
|
g: torch.Tensor,
|
|
beta: torch.Tensor,
|
|
*,
|
|
ssm_states: torch.Tensor,
|
|
cache_indices: torch.Tensor,
|
|
query_start_loc: torch.Tensor,
|
|
A_log: Optional[torch.Tensor] = None,
|
|
dt_bias: Optional[torch.Tensor] = None,
|
|
lower_bound: Optional[float] = None,
|
|
) -> torch.Tensor:
|
|
flash_kda = _load_flash_kda()
|
|
|
|
# Input shapes (varlen, B == 1, matching chunk_kda's contract):
|
|
# q, k = [1, packed_seq, H, K] v = [1, packed_seq, HV, V]
|
|
# g = [1, packed_seq, HV, K] beta = [1, packed_seq, H]
|
|
# flash_kda wants these 4D tensors directly and RAW (it fuses l2norm /
|
|
# beta sigmoid / gate activation in-kernel).
|
|
num_heads = q.shape[2]
|
|
head_dim = q.shape[3]
|
|
scale = head_dim**-0.5
|
|
|
|
q = q.contiguous()
|
|
k = k.contiguous()
|
|
v = v.contiguous()
|
|
g = g.contiguous()
|
|
|
|
# KimiDeltaAttention.forward already applies sigmoid to beta on the
|
|
# prefill path, but flash_kda expects beta LOGITS (it sigmoids
|
|
# internally). Invert back so the kernel recovers the intended value:
|
|
# sigmoid(logit(p)) == p. (triton/cuLA consume the post-sigmoid beta.)
|
|
beta = torch.logit(beta.float().clamp_(1e-7, 1.0 - 1e-7)).to(torch.bfloat16)
|
|
beta = beta.contiguous()
|
|
|
|
# flash_kda wants A_log [H] fp32 and dt_bias [H, K] fp32. The model
|
|
# stores A_log as [1, 1, H, 1] and dt_bias as 1D [H*K], so reshape both.
|
|
A_log = A_log.reshape(-1).float().contiguous()
|
|
if dt_bias is not None:
|
|
dt_bias = dt_bias.reshape(num_heads, -1).float().contiguous()
|
|
|
|
# cu_seqlens must be int64 for flash_kda (FLA casts to long).
|
|
cu_seqlens = query_start_loc.to(torch.int64)
|
|
|
|
# flash_kda varlen state is [N, H, V, K] -- the SAME layout as sglang's
|
|
# KDA pool, so no transpose is needed. Advanced indexing copies, so the
|
|
# final state is written back in-place below (matching chunk_kda).
|
|
initial_state = ssm_states[cache_indices].contiguous()
|
|
|
|
out_buf = torch.empty_like(v)
|
|
final_state = torch.empty_like(initial_state)
|
|
|
|
flash_kda.fwd(
|
|
q,
|
|
k,
|
|
v,
|
|
g,
|
|
beta,
|
|
scale,
|
|
out_buf,
|
|
A_log,
|
|
dt_bias,
|
|
lower_bound,
|
|
initial_state=initial_state,
|
|
final_state=final_state,
|
|
cu_seqlens=cu_seqlens,
|
|
)
|
|
|
|
ssm_states[cache_indices] = final_state
|
|
|
|
# out_buf is already [1, packed_seq, HV, V].
|
|
return out_buf
|