Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
84db3fd
Improve MTP and large concurrency performance
clintg6 Jul 24, 2026
cf74632
Address merge conflict and reviewer suggestions
clintg6 Jul 24, 2026
aadadc9
Merge branch 'main' into feat/triton-sparse-mla
clintg6 Jul 24, 2026
fd9755e
Merge branch 'main' into feat/triton-sparse-mla
clintg6 Aug 4, 2026
3c450d5
Update cta gate to improve prefill performance
clintg6 Aug 4, 2026
38aef8a
Remove contiguous copies from both prefill and decode kernels
clintg6 Aug 4, 2026
67f2a3b
Merge branch 'main' into feat/triton-sparse-mla
clintg6 Aug 10, 2026
f9e7809
Resolve main merge conflict for Triton DSA
clintg6 Aug 10, 2026
605c177
Implement async copy management in Triton sparse MLA
clintg6 Aug 13, 2026
a6c9aff
Update async copy behavior to address triton 3.7 regression
clintg6 Aug 13, 2026
5f8898c
Merge branch 'main' into feat/triton-sparse-mla
HaiShaw Aug 15, 2026
3bd32fc
Merge branch 'main' into feat/triton-sparse-mla
HaiShaw Aug 16, 2026
d75eea9
Merge branch 'main' into feat/triton-sparse-mla
HaiShaw Aug 17, 2026
92da23a
fix undefined symbols
HaiShaw Aug 17, 2026
3f9dd6b
Merge branch 'main' into feat/triton-sparse-mla
HaiShaw Aug 17, 2026
cb3cb14
Merge branch 'main' into feat/triton-sparse-mla
HaiShaw Aug 17, 2026
8078584
Merge branch 'main' into feat/triton-sparse-mla
HaiShaw Aug 17, 2026
f1c2f08
preserve compatible ROCm backend defaults
clintg6 Aug 17, 2026
665a5b9
apply dtype-compatible ROCm ds backend default
clintg6 Aug 17, 2026
07e9260
Fix merge conflict
clintg6 Aug 24, 2026
2126799
Merge branch 'main' into feat/triton-sparse-mla
clintg6 Aug 24, 2026
c5a54ea
Merge branch 'main' into feat/triton-sparse-mla
HaiShaw Aug 25, 2026
d9f3bd6
Fix typo
clintg6 Aug 25, 2026
2f11645
Fix Triton sparse MLA edge cases and drop aiter dependency for CU count
clintg6 Aug 29, 2026
285d57d
Merge branch 'main' into feat/triton-sparse-mla
clintg6 Aug 29, 2026
ffce301
Fix linting issue
clintg6 Aug 29, 2026
2a09e07
Fix linting issue
clintg6 Aug 29, 2026
626cec3
[AMD] Enable BF16 support in Triton sparse MLA
clintg6 Aug 31, 2026
2e5c110
[AMD] Make Triton sparse MLA BF16 tile size LDS-aware
clintg6 Sep 1, 2026
d065757
Merge branch 'main' into feat/triton-sparse-mla
clintg6 Sep 1, 2026
38609a7
[AMD] Fix MI300X FP8 Triton sparse MLA query dtype
Sep 1, 2026
7d34c0e
[AMD] Keep sparse MLA FP8 dot operands consistent
clintg6 Sep 1, 2026
38c26ca
Merge branch 'main' into feat/triton-sparse-mla
HaiShaw Sep 1, 2026
2aefc32
Merge branch 'main' into feat/triton-sparse-mla
clintg6 Sep 3, 2026
91f70a4
Fix linting issue
clintg6 Sep 3, 2026
9ae98e7
Fix linting issue
clintg6 Sep 3, 2026
77bf906
Merge branch 'main' into feat/triton-sparse-mla
clintg6 Sep 3, 2026
ca7797c
Merge branch 'main' into feat/triton-sparse-mla
clintg6 Sep 4, 2026
feea2f8
Merge branch 'main' into feat/triton-sparse-mla
clintg6 Sep 6, 2026
7182bce
Merge upstream main and resolve conflicts
clintg6 Sep 8, 2026
064fc7c
Merge branch 'main' into feat/triton-sparse-mla
clintg6 Sep 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,108 changes: 1,031 additions & 77 deletions python/sglang/kernels/ops/attention/dsa/triton_sparse_mla.py

Large diffs are not rendered by default.

742 changes: 742 additions & 0 deletions python/sglang/kernels/ops/attention/dsa/triton_sparse_mla_decode.py

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions python/sglang/srt/arg_groups/fields/exec_.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ class ExecKernel:
"flashinfer_sparse_mla",
"fa3",
"tilelang",
"triton",
"aiter",
"trtllm",
],
Expand Down Expand Up @@ -224,6 +225,7 @@ class ExecKernel:
"flashinfer_sparse_mla",
"fa3",
"tilelang",
"triton",
"aiter",
"trtllm",
],
Expand Down
2 changes: 1 addition & 1 deletion python/sglang/srt/arg_groups/hisparse_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"bfloat16": {"flashmla_sparse"},
"fp8_e4m3": {"flashmla_kv", "flashinfer_sparse_mla"},
}
HISPARSE_ROCM_DSA_BACKENDS = {"tilelang", "aiter"}
HISPARSE_ROCM_DSA_BACKENDS = {"tilelang", "triton", "aiter"}
HISPARSE_KV_CACHE_DTYPES = ("bfloat16", "fp8_e4m3")


Expand Down
57 changes: 42 additions & 15 deletions python/sglang/srt/arg_groups/overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,29 +681,48 @@ def _dsa_kv_cache_dtype_default(view: Any) -> dict:
return {}


def _check_tilelang_dsa_fp8_kv(
def _check_dsa_backend_constraints(
kv_cache_dtype: str,
prefill_backend: Optional[str],
decode_backend: Optional[str],
*,
hip: bool,
) -> None:
"""tilelang's fp8 KV path is ROCm-only; the CUDA kernel hardcodes bfloat16.
Reject here instead of crashing at decode CUDA-graph capture."""
if (
not hip
and kv_cache_dtype == "fp8_e4m3"
and "tilelang" in {prefill_backend, decode_backend}
):
"""Validate DSA backend / platform / kv-cache-dtype constraints."""
chosen = {prefill_backend, decode_backend}

rocm_only = {"triton"} & chosen
if not hip and rocm_only:
raise ValueError(
f"The {'/'.join(sorted(rocm_only))} DSA backend is only supported on "
"ROCm/HIP. Pick an alternative DSA backend for CUDA "
"(flashmla_kv on Hopper, trtllm on Blackwell)."
)

cuda_fp8_unsupported = {"tilelang"} & chosen
if not hip and kv_cache_dtype == "fp8_e4m3" and cuda_fp8_unsupported:
raise ValueError(
"The tilelang DSA prefill/decode kernels only support an fp8_e4m3 KV "
"cache on ROCm/HIP; on CUDA they require a bfloat16 KV cache. Use "
"--kv-cache-dtype bfloat16 with the tilelang backend, or keep "
f"The {'/'.join(sorted(cuda_fp8_unsupported))} DSA prefill/decode kernels "
"only support an fp8_e4m3 KV cache on ROCm/HIP; on CUDA they require "
"a bfloat16 KV cache. Use --kv-cache-dtype bfloat16, or keep "
"--kv-cache-dtype fp8_e4m3 and pick an fp8-capable DSA backend "
"(flashmla_kv on Hopper, trtllm on Blackwell)."
)


def _check_tilelang_dsa_fp8_kv(
kv_cache_dtype: str,
prefill_backend: Optional[str],
decode_backend: Optional[str],
*,
hip: bool,
) -> None:
"""Backward-compatible entry point for the TileLang DSA validation."""
_check_dsa_backend_constraints(
kv_cache_dtype, prefill_backend, decode_backend, hip=hip
)


@register_post_process
def _dsa_split_backend_resolution(view: Any) -> dict:
"""Slot pass in the DSA arm: default the DSA prefill/decode split
Expand Down Expand Up @@ -795,15 +814,23 @@ def _dsa_split_backend_resolution(view: Any) -> dict:
declared["dsa_decode_backend"] = backend
prefill = declared.get("dsa_prefill_backend", view.dsa_prefill_backend)
decode = declared.get("dsa_decode_backend", view.dsa_decode_backend)
# The hisparse allow-list in hisparse_hook is platform- but not
# dtype-aware, so an explicitly requested backend still has to clear the
# shared backend/kv-cache-dtype rules before this arm returns early.
_check_dsa_backend_constraints(
kv_cache_dtype, prefill, decode, hip=get_platform().is_hip
)
logger.warning(
f"HiSparse enabled ({kv_cache_dtype}): using DSA backends "
f"prefill={prefill}, decode={decode}."
)
return declared

if not user_set_prefill and not user_set_decode and get_platform().is_hip:
declared["dsa_prefill_backend"] = "tilelang"
declared["dsa_decode_backend"] = "tilelang"
if get_platform().is_hip:
if not user_set_prefill:
declared["dsa_prefill_backend"] = "triton"
if not user_set_decode:
declared["dsa_decode_backend"] = "triton"
elif kv_cache_dtype == "fp8_e4m3":
# Blackwell FP8 defaults to trtllm; Hopper FP8 to flashmla_kv.
default = "trtllm" if major >= 10 else "flashmla_kv"
Expand All @@ -820,7 +847,7 @@ def _dsa_split_backend_resolution(view: Any) -> dict:

prefill = declared.get("dsa_prefill_backend", view.dsa_prefill_backend)
decode = declared.get("dsa_decode_backend", view.dsa_decode_backend)
_check_tilelang_dsa_fp8_kv(
_check_dsa_backend_constraints(
kv_cache_dtype, prefill, decode, hip=get_platform().is_hip
)
logger.warning(
Expand Down
79 changes: 45 additions & 34 deletions python/sglang/srt/layers/attention/dsa_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,21 +90,13 @@
from sglang.srt.model_executor.forward_batch_info import ForwardBatch, ForwardMode
from sglang.srt.runtime_context import get_buffer, get_exec, get_parallel, get_spec
from sglang.srt.utils import (
get_bool_env_var,
is_cuda,
is_gfx95_supported,
is_hip,
is_xpu,
print_warning_once,
)

logger = logging.getLogger(__name__)

# Opt-in (default off): route the fp8 sparse-MLA prefill path through the Triton
# per-query flash kernel instead of TileLang. Validated on gfx950 (GLM-5.1 @
# TP4: 16 heads, d_v=512, tail=64). Reads q_nope/q_rope directly (skips the
# concat). Enable with SGLANG_DSA_TRITON_PREFILL=1. Decode stays on TileLang.
_DSA_TRITON_PREFILL = get_bool_env_var("SGLANG_DSA_TRITON_PREFILL")
_IS_GFX95 = is_gfx95_supported()

if is_cuda():
Expand Down Expand Up @@ -302,6 +294,7 @@ def _cat(tensors: list[torch.Tensor], dim: int = -1) -> torch.Tensor:
"flashinfer_sparse_mla",
"fa3",
"tilelang",
"triton",
"trtllm",
"intel_xpu",
]
Expand Down Expand Up @@ -2123,32 +2116,6 @@ def forward_extend(

if dsa_impl == "tilelang":
if q_rope is not None:
# Triton prefill kernel reads q_nope/q_rope directly, skipping
# the concat (it splits q into main/tail internally anyway).
# Gated to gfx950 + the validated shape (16 heads, d_v=512,
# tail=64, topk=2048); everything else uses TileLang.
if (
_DSA_TRITON_PREFILL
and _IS_GFX95
and kv_cache.dtype in (torch.float8_e4m3fn, torch.float8_e4m3fnuz)
and layer.tp_q_head_num == 16
and layer.v_head_dim == 512
and (layer.head_dim - layer.v_head_dim) == 64
and page_table_1.shape[-1] == 2048
and q_nope.shape[0] >= 512
):
from sglang.kernels.ops.attention.dsa.triton_sparse_mla import (
triton_sparse_mla_fwd,
)

return triton_sparse_mla_fwd(
q_nope=q_nope,
q_rope=q_rope,
kv=kv_cache,
indices=page_table_1.unsqueeze(1),
sm_scale=layer.scaling,
d_v=layer.v_head_dim,
)
# Cat-skip, as in forward_decode: q_rope=None means the caller
# already handed us the concatenated form and q_all is a
# zero-copy view of it. `not _is_hip` keeps CUDA byte-identical.
Expand All @@ -2161,6 +2128,19 @@ def forward_extend(
sm_scale=layer.scaling,
v_head_dim=layer.v_head_dim,
)
elif dsa_impl == "triton":
from sglang.kernels.ops.attention.dsa.triton_sparse_mla import (
triton_sparse_mla_fwd,
)

return triton_sparse_mla_fwd(
Comment thread
HaiShaw marked this conversation as resolved.
q_nope=q_nope,
q_rope=q_rope,
kv=kv_cache,
indices=page_table_1.unsqueeze(1),
sm_scale=layer.scaling,
d_v=layer.v_head_dim,
)
elif dsa_impl in ("flashmla_sparse", "flashmla_sparse_q8"):
if topk_transform_method == TopkTransformMethod.RAGGED:
_has_prefix = any(forward_batch.extend_prefix_lens_cpu)
Expand Down Expand Up @@ -2457,6 +2437,15 @@ def forward_decode(
sm_scale=layer.scaling,
v_head_dim=layer.v_head_dim,
)
elif dsa_impl == "triton":
return self._forward_triton_decode(
q_nope=q_nope,
q_rope=q_rope,
kv_cache=kv_cache,
v_head_dim=layer.v_head_dim,
page_table_1=page_table_1,
sm_scale=layer.scaling,
)
elif dsa_impl == "fa3":
return self._forward_fa3(
q_rope=q_rope,
Expand Down Expand Up @@ -3115,6 +3104,28 @@ def _forward_tilelang(
d_v=v_head_dim,
)

def _forward_triton_decode(
self,
q_nope: torch.Tensor,
q_rope: torch.Tensor,
kv_cache: torch.Tensor,
v_head_dim: int,
page_table_1: torch.Tensor,
sm_scale: float,
) -> torch.Tensor:
from sglang.kernels.ops.attention.dsa.triton_sparse_mla_decode import (
triton_sparse_mla_decode_splitk,
)

return triton_sparse_mla_decode_splitk(
q_nope=q_nope,
q_rope=q_rope,
kv=kv_cache,
indices=page_table_1.unsqueeze(1),
sm_scale=sm_scale,
d_v=v_head_dim,
)

def _forward_intel_xpu_sparse_decode(
self,
q_nope: torch.Tensor,
Expand Down
4 changes: 2 additions & 2 deletions python/sglang/srt/mem_cache/kv_cache_configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2555,8 +2555,8 @@ def calculate_mla_kv_cache_dim(
# On HIP, TileLang and AITER DSA kernels consume the raw MLA KV layout:
# nope(512 fp8) + rope(64 fp8), without extra per-block scales.
if _is_hip and (
get_exec().kernel.dsa_prefill_backend in ("tilelang", "aiter")
or get_exec().kernel.dsa_decode_backend in ("tilelang", "aiter")
get_exec().kernel.dsa_prefill_backend in ("tilelang", "triton", "aiter")
or get_exec().kernel.dsa_decode_backend in ("tilelang", "triton", "aiter")
):
return kv_cache_dim

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -890,14 +890,14 @@ def forward_absorb_rocm_core(

def _skip_rope_for_dsa_tilelang_fused(self: DeepseekV2AttentionMLA) -> bool:
"""
Check if we should skip rope and use fused rope+cache path for TileLang DSA on gfx95.
Check if we should skip rope and use fused rope+cache path for TileLang/Triton DSA on gfx95.
"""
return (
_use_aiter_gfx95
and self.current_attention_backend in ("dsa", "nsa")
and (
get_exec().kernel.dsa_decode_backend == "tilelang"
or get_exec().kernel.dsa_prefill_backend == "tilelang"
get_exec().kernel.dsa_decode_backend in ("tilelang", "triton")
or get_exec().kernel.dsa_prefill_backend in ("tilelang", "triton")
)
)

Expand Down
6 changes: 3 additions & 3 deletions test/registered/unit/test_model_overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -1951,12 +1951,12 @@ def _view(arch="DeepseekV32ForCausalLM", learnable_sink=False, **kw):
override_platform(is_hip=True),
patch("torch.cuda.get_device_capability", return_value=(9, 4)),
):
# ROCm with both unset -> tilelang
# ROCm with both unset -> Triton for FP8 and BF16 KV cache.
self.assertEqual(
_dsa_split_backend_resolution(_view(kv_cache_dtype="bfloat16")),
{
"dsa_prefill_backend": "tilelang",
"dsa_decode_backend": "tilelang",
"dsa_prefill_backend": "triton",
"dsa_decode_backend": "triton",
},
)

Expand Down
Loading