Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

357 lines
13 KiB
Python

# Copyright (c) 2026 LightSeek Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from __future__ import annotations
"""Attention backend for the tokenspeed-mla CuTe DSL kernels on Blackwell.
Subclasses :class:`TRTLLMMLABackend` and overrides only ``_run_decode_kernel``
and ``_run_prefill_kernel``. All metadata, KV-cache layout, CUDA-graph
plumbing, FP8 quantize/rope, draft-extend padding, and chunked-prefix
dispatch are inherited unchanged from the parent.
"""
import logging
from typing import TYPE_CHECKING, Optional
import torch
from sglang.jit_kernel.fp8_quantize import fp8_quantize
from sglang.jit_kernel.mla_kv_pack_quantize_fp8 import mla_kv_pack_quantize_fp8
from sglang.jit_kernel.utils import is_arch_support_pdl
from sglang.srt.layers.attention.trtllm_mla_backend import (
TRTLLMMLABackend,
TRTLLMMLAMultiStepDraftBackend,
)
from sglang.srt.utils import is_flashinfer_available, is_tokenspeed_mla_available
if is_flashinfer_available():
import flashinfer.rope as _flashinfer_rope
if is_tokenspeed_mla_available():
import tokenspeed_mla
if TYPE_CHECKING:
from sglang.srt.layers.radix_attention import RadixAttention
from sglang.srt.model_executor.forward_batch_info import ForwardBatch
from sglang.srt.model_executor.model_runner import ModelRunner
from sglang.srt.models.deepseek_v2 import DeepseekV2AttentionMLA
logger = logging.getLogger(__name__)
# Workspace upper bound for tokenspeed_mla_decode:
# num_sms * num_heads * max_q_len * (kv_lora_rank + 1) * sizeof(float32)
# MAX_Q_LEN=8 covers EAGLE3 num_draft_tokens=4 plus headroom.
_TOKENSPEED_MAX_Q_LEN = 8
def _get_tokenspeed_workspace(
device: torch.device, num_heads: int, kv_lora_rank: int
) -> torch.Tensor:
from sglang.srt.runtime_context import get_resources
needed = (
tokenspeed_mla.get_num_sm(device)
* num_heads
* _TOKENSPEED_MAX_Q_LEN
* (kv_lora_rank + 1)
* 4
)
buffers = get_resources().buffers
key = f"tokenspeed_mla_workspace:{device}"
existing = buffers.get(key)
if existing is None or existing.numel() < needed:
buffers[key] = torch.empty(needed, dtype=torch.int8, device=device)
return buffers[key]
# TODO(Qiaolin-Yu): Merge this attention backend into trtllm_mla_backend.py
# once the same CuteDSL kernels in flashinfer_trtllm are stable
# and there is no performance gap compared to this backend.
class TokenspeedMLABackend(TRTLLMMLABackend):
"""tokenspeed-mla CuTe DSL attention backend (Blackwell SM100, FP8 KV)."""
def __init__(
self,
model_runner: ModelRunner,
skip_prefill: bool = False,
kv_indptr_buf: Optional[torch.Tensor] = None,
q_indptr_decode_buf: Optional[torch.Tensor] = None,
):
super().__init__(
model_runner,
skip_prefill,
kv_indptr_buf,
q_indptr_decode_buf,
)
if self.data_type != torch.float8_e4m3fn:
raise ValueError(
"tokenspeed_mla backend requires --kv-cache-dtype fp8_e4m3, "
f"got data_type={self.data_type}."
)
if self.page_size not in (32, 64):
raise ValueError(
"tokenspeed_mla backend requires page_size in {32, 64}, "
f"got page_size={self.page_size}."
)
self._tokenspeed_workspace: Optional[torch.Tensor] = None
if is_tokenspeed_mla_available():
self._tokenspeed_workspace = _get_tokenspeed_workspace(
self.device, self.num_q_heads, self.kv_lora_rank
)
# Pre-JIT the prefill kernel variants. Each cute.compile takes 1-2
# min; without warm-up the first request trips the 300 s scheduler
# watchdog.
_compile_prefill_kernel = tokenspeed_mla.mla_prefill._compile_prefill_kernel
_compiled_kernels = tokenspeed_mla.mla_prefill._compiled_kernels
head_dim_qk = self.qk_nope_head_dim + self.qk_rope_head_dim
enable_ex2_emulation = tokenspeed_mla.mla_prefill._enable_ex2_emulation()
use_pdl = is_arch_support_pdl()
for is_causal in (True, False):
for return_lse in (True, False):
# Non-causal is only entered from the chunked-prefix
# branch, which always asks for the LSE.
if is_causal is False and return_lse is False:
continue
# Runtime feeds fp8_e4m3fn q/k/v
config = (
torch.float8_e4m3fn,
head_dim_qk,
self.v_head_dim,
is_causal,
return_lse,
use_pdl,
enable_ex2_emulation,
)
if config in _compiled_kernels:
continue
_compiled_kernels[config] = _compile_prefill_kernel(
torch.float8_e4m3fn,
head_dim_qk,
self.v_head_dim,
is_causal,
return_lse,
use_pdl=use_pdl,
enable_ex2_emulation=enable_ex2_emulation,
)
def _fused_rope_fp8_quantize(
self,
q_nope: torch.Tensor,
q_pe: torch.Tensor,
k_nope: torch.Tensor,
k_pe: torch.Tensor,
cos_sin_cache: torch.Tensor,
positions: torch.Tensor,
is_neox: bool,
qk_nope_head_dim: int,
qk_rope_head_dim: int,
) -> tuple[torch.Tensor, torch.Tensor]:
"""Fused RoPE + FP8 quantize that also packs nope+pe along the last
dim, so FMHA consumes contig FP8 Q/K without an extra concat or cast.
"""
num_heads = q_nope.shape[1]
seq_len = q_nope.shape[0]
q_fp8 = torch.empty(
(seq_len, num_heads, qk_nope_head_dim + qk_rope_head_dim),
dtype=torch.float8_e4m3fn,
device=q_nope.device,
)
k_fp8 = torch.empty(
(seq_len, num_heads, qk_nope_head_dim + qk_rope_head_dim),
dtype=torch.float8_e4m3fn,
device=k_nope.device,
)
if seq_len == 0:
return q_fp8, k_fp8
# Broadcast the shared latent k_pe across heads — RoPE is position-only
# so per-head outputs are identical, and the cache write below reuses
# head 0.
if k_pe.dim() == 3 and k_pe.shape[1] == 1:
k_pe_expanded = k_pe.expand(-1, num_heads, -1)
else:
k_pe_expanded = k_pe
_flashinfer_rope.mla_rope_quantize_fp8(
q_rope=q_pe,
k_rope=k_pe_expanded,
q_nope=q_nope,
k_nope=k_nope,
cos_sin_cache=cos_sin_cache,
pos_ids=positions,
is_neox=is_neox,
quantize_dtype=torch.float8_e4m3fn,
q_rope_out=q_fp8[..., qk_nope_head_dim:],
k_rope_out=k_fp8[..., qk_nope_head_dim:],
q_nope_out=q_fp8[..., :qk_nope_head_dim],
k_nope_out=k_fp8[..., :qk_nope_head_dim],
quant_scale_q=1.0,
quant_scale_kv=1.0,
enable_pdl=is_arch_support_pdl(),
)
return q_fp8, k_fp8
def prepare_prefill_qkv(
self,
*,
q: torch.Tensor,
q_pe: torch.Tensor,
kv_a: torch.Tensor,
k_pe: torch.Tensor,
positions: torch.Tensor,
layer: DeepseekV2AttentionMLA,
forward_batch: ForwardBatch,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
"""Build FP8 (Q, K, V) for the FMHA kernel and write FP8 KV cache."""
kv = layer.kv_b_proj(kv_a)[0]
kv = kv.view(
-1, layer.num_local_heads, layer.qk_nope_head_dim + layer.v_head_dim
)
k_nope = kv[..., : layer.qk_nope_head_dim]
v_bf16 = kv[..., layer.qk_nope_head_dim :]
q_nope = q[..., : layer.qk_nope_head_dim]
q_fp8, k_fp8 = self._fused_rope_fp8_quantize(
q_nope=q_nope,
q_pe=q_pe,
k_nope=k_nope,
k_pe=k_pe,
cos_sin_cache=layer.rotary_emb.cos_sin_cache,
positions=positions,
is_neox=getattr(layer.rotary_emb, "is_neox_style", True),
qk_nope_head_dim=layer.qk_nope_head_dim,
qk_rope_head_dim=layer.qk_rope_head_dim,
)
v_fp8 = fp8_quantize(v_bf16, enable_pdl=is_arch_support_pdl())
# k_pe is shared across heads (RoPE is position-only), so head 0
# reproduces the original [tokens, 1, qk_rope] latent layout.
kv_a_fp8 = fp8_quantize(kv_a, enable_pdl=is_arch_support_pdl())
k_pe_fp8 = k_fp8[:, 0:1, layer.qk_nope_head_dim :]
self.token_to_kv_pool.set_mla_kv_buffer(
layer.attn_mha,
forward_batch.out_cache_loc,
kv_a_fp8.unsqueeze(1),
k_pe_fp8,
)
return q_fp8, k_fp8, v_fp8
def pack_prefix_chunk_kv(
self,
k_nope: torch.Tensor,
k_pe: torch.Tensor,
v: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor]:
"""Pack strided ``k_nope``+``k_pe`` into contig FP8 K and quantize
strided ``v`` into contig FP8 V in a single kernel.
"""
return mla_kv_pack_quantize_fp8(
k_nope, k_pe, v, enable_pdl=is_arch_support_pdl()
)
def _run_decode_kernel(
self,
query: torch.Tensor,
kv_cache: torch.Tensor,
block_tables: torch.Tensor,
seq_lens: torch.Tensor,
max_seq_len: int,
layer: RadixAttention,
) -> torch.Tensor:
k_scale = getattr(layer, "k_scale_float", None)
if k_scale is None:
k_scale = 1.0
softmax_scale = float(layer.scaling) * float(k_scale)
output_scale = float(k_scale)
seq_lens_i32 = (
seq_lens if seq_lens.dtype == torch.int32 else seq_lens.to(torch.int32)
)
return tokenspeed_mla.tokenspeed_mla_decode(
query=query,
kv_cache=kv_cache,
workspace_buffer=self._tokenspeed_workspace,
kv_lora_rank=self.kv_lora_rank,
qk_rope_head_dim=self.qk_rope_head_dim,
block_tables=block_tables,
seq_lens=seq_lens_i32,
max_seq_len=int(max_seq_len),
softmax_scale=softmax_scale,
output_scale=output_scale,
enable_pdl=is_arch_support_pdl(),
)
def _run_prefill_kernel(
self,
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
layer: RadixAttention,
batch_size: int,
cum_seq_lens_q: torch.Tensor,
max_q_len: int,
seq_lens_kv: torch.Tensor,
cum_seq_lens_kv: torch.Tensor,
max_kv_len: int,
is_causal: bool,
return_lse: bool,
out_buffer: torch.Tensor,
o_sf_scale: float = 1.0,
): # Q/K/V arrive already in FP8 via the model-side fused path
# (prepare_prefill_qkv / pack_prefix_chunk_kv); no quantize here.
return tokenspeed_mla.tokenspeed_mla_prefill(
query=q,
key=k,
value=v,
seq_lens=seq_lens_kv,
cum_seq_lens=cum_seq_lens_kv,
max_seq_len=int(max_kv_len),
batch_size=int(batch_size),
softmax_scale=float(layer.scaling),
is_causal=is_causal,
return_lse=return_lse,
cum_seq_lens_q=cum_seq_lens_q,
max_seq_len_q=int(max_q_len),
enable_pdl=is_arch_support_pdl(),
)
class TokenspeedMLAMultiStepDraftBackend(TRTLLMMLAMultiStepDraftBackend):
"""Multi-step draft backend for tokenspeed_mla used by EAGLE."""
def __init__(
self, model_runner: ModelRunner, topk: int, speculative_num_steps: int
):
super().__init__(model_runner, topk, speculative_num_steps)
# Parent populates self.attn_backends with TRT-LLM instances; replace
# them with tokenspeed instances sharing the parent's index buffers.
for i in range(self.speculative_num_steps - 1):
self.attn_backends[i] = TokenspeedMLABackend(
model_runner,
skip_prefill=True,
kv_indptr_buf=self.kv_indptr[i],
q_indptr_decode_buf=self.q_indptr_decode,
)