mirror of
https://gitee.com/trusted-list/sglang.git
synced 2026-10-28 08:34:56 +08:00
[Refactor] attention: Route every FlashAttention call through the selected kernel (#46695)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Alex Nails <alex.nails@radixark.ai>
This commit is contained in:
3 files changed
+105
-74
No files matched your search
@@ -80,6 +80,7 @@ def flash_attn_varlen_func(
|
||||
num_splits: int = 1,
|
||||
pack_gqa: Optional[bool] = None,
|
||||
score_mod: Optional[Callable] = None,
|
||||
mask_mod: Optional[Callable] = None,
|
||||
aux_tensors: Optional[list] = None,
|
||||
q_descale: Optional[torch.Tensor] = None,
|
||||
k_descale: Optional[torch.Tensor] = None,
|
||||
@@ -162,6 +163,7 @@ def flash_attn_varlen_func(
|
||||
num_splits=num_splits,
|
||||
pack_gqa=pack_gqa,
|
||||
score_mod=score_mod,
|
||||
mask_mod=mask_mod,
|
||||
aux_tensors=aux_tensors,
|
||||
return_lse=return_softmax_lse,
|
||||
out=out,
|
||||
@@ -211,6 +213,7 @@ def flash_attn_with_kvcache(
|
||||
sm_margin: int = 0,
|
||||
sinks: Optional[torch.Tensor] = None,
|
||||
score_mod: Optional[Callable] = None,
|
||||
mask_mod: Optional[Callable] = None,
|
||||
aux_tensors: Optional[list] = None,
|
||||
sfq: Optional[torch.Tensor] = None,
|
||||
sfk: Optional[torch.Tensor] = None,
|
||||
@@ -246,6 +249,7 @@ def flash_attn_with_kvcache(
|
||||
for value in (
|
||||
qv,
|
||||
score_mod,
|
||||
mask_mod,
|
||||
aux_tensors,
|
||||
q_descale,
|
||||
k_descale,
|
||||
@@ -304,6 +308,7 @@ def flash_attn_with_kvcache(
|
||||
pack_gqa=pack_gqa,
|
||||
learnable_sink=sinks,
|
||||
score_mod=score_mod,
|
||||
mask_mod=mask_mod,
|
||||
aux_tensors=aux_tensors,
|
||||
q_descale=q_descale,
|
||||
k_descale=k_descale,
|
||||
|
||||
@@ -61,11 +61,6 @@ if TYPE_CHECKING:
|
||||
|
||||
from sgl_kernel import merge_state_v2
|
||||
|
||||
from sglang.kernels.ops.attention.flash_attention import (
|
||||
flash_attn_varlen_func,
|
||||
flash_attn_with_kvcache,
|
||||
)
|
||||
|
||||
|
||||
def _should_disable_scheduler_metadata_precompute() -> bool:
|
||||
return bool(get_parallel().enable_prefill_cp or get_parallel().attn_dp_enabled)
|
||||
@@ -279,10 +274,11 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
self.fa_impl_ver = fa_impl_ver
|
||||
device_capability = get_device_capability()
|
||||
if self.fa_impl_ver == 3:
|
||||
from sgl_kernel.flash_attn import (
|
||||
from sgl_kernel.flash_attn import get_scheduler_metadata
|
||||
|
||||
from sglang.kernels.ops.attention.flash_attention_v3 import (
|
||||
flash_attn_varlen_func,
|
||||
flash_attn_with_kvcache,
|
||||
get_scheduler_metadata,
|
||||
)
|
||||
|
||||
self._get_scheduler_metadata = get_scheduler_metadata
|
||||
@@ -317,6 +313,7 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
else:
|
||||
raise ValueError(f"Invalid version: {self.fa_impl_ver=}")
|
||||
|
||||
# Every kernel call goes through these; a subclass swaps the FA build here.
|
||||
self.flash_attn_varlen_func = flash_attn_varlen_func
|
||||
self.flash_attn_with_kvcache = flash_attn_with_kvcache
|
||||
|
||||
@@ -1701,7 +1698,7 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
def _fa_cp_attn(
|
||||
q_chunk, cu_seqlens_q_cp, cache_seqlens_cp, max_seqlen_q_cp
|
||||
):
|
||||
return flash_attn_with_kvcache(
|
||||
return self.flash_attn_with_kvcache(
|
||||
q=q_chunk,
|
||||
k_cache=key_cache,
|
||||
v_cache=value_cache,
|
||||
@@ -1716,7 +1713,6 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
softcap=layer.logit_cap,
|
||||
return_softmax_lse=use_cascade_attn,
|
||||
num_splits=self.num_splits,
|
||||
ver=self.fa_impl_ver,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@@ -1786,7 +1782,7 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
**kwargs,
|
||||
)
|
||||
else:
|
||||
result = flash_attn_with_kvcache(
|
||||
result = self.flash_attn_with_kvcache(
|
||||
q=q.contiguous().view(-1, layer.tp_q_head_num, layer.head_dim),
|
||||
k_cache=key_cache,
|
||||
v_cache=value_cache,
|
||||
@@ -1802,7 +1798,6 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
return_softmax_lse=use_cascade_attn or return_lse,
|
||||
num_splits=self.num_splits,
|
||||
out=_fa_out,
|
||||
ver=self.fa_impl_ver,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@@ -1837,30 +1832,31 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
softmax_scale=layer.scaling,
|
||||
)
|
||||
elif use_cascade_attn:
|
||||
o_expand, softmax_lse_expand, *rest_expand = flash_attn_with_kvcache(
|
||||
q=q.contiguous().view(-1, layer.tp_q_head_num, layer.head_dim),
|
||||
# The suffix table stores physical token slots, so expose
|
||||
# the paged cache as page-size-one blocks.
|
||||
k_cache=paged_view(
|
||||
key_cache.view(-1, layer.tp_k_head_num, layer.head_dim), 1
|
||||
),
|
||||
v_cache=paged_view(
|
||||
value_cache.view(-1, layer.tp_v_head_num, layer.v_head_dim),
|
||||
1,
|
||||
),
|
||||
page_table=self.forward_metadata_spec_decode_expand.page_table,
|
||||
cache_seqlens=self.forward_metadata_spec_decode_expand.cache_seqlens_int32,
|
||||
cu_seqlens_q=self.forward_metadata_spec_decode_expand.cu_seqlens_q,
|
||||
cu_seqlens_k_new=self.forward_metadata_spec_decode_expand.cu_seqlens_k,
|
||||
max_seqlen_q=self.forward_metadata_spec_decode_expand.max_seq_len_q,
|
||||
softmax_scale=layer.scaling,
|
||||
causal=False,
|
||||
window_size=window_size,
|
||||
softcap=layer.logit_cap,
|
||||
return_softmax_lse=True,
|
||||
num_splits=self.num_splits,
|
||||
ver=self.fa_impl_ver,
|
||||
**kwargs,
|
||||
o_expand, softmax_lse_expand, *rest_expand = (
|
||||
self.flash_attn_with_kvcache(
|
||||
q=q.contiguous().view(-1, layer.tp_q_head_num, layer.head_dim),
|
||||
# The suffix table stores physical token slots, so expose
|
||||
# the paged cache as page-size-one blocks.
|
||||
k_cache=paged_view(
|
||||
key_cache.view(-1, layer.tp_k_head_num, layer.head_dim), 1
|
||||
),
|
||||
v_cache=paged_view(
|
||||
value_cache.view(-1, layer.tp_v_head_num, layer.v_head_dim),
|
||||
1,
|
||||
),
|
||||
page_table=self.forward_metadata_spec_decode_expand.page_table,
|
||||
cache_seqlens=self.forward_metadata_spec_decode_expand.cache_seqlens_int32,
|
||||
cu_seqlens_q=self.forward_metadata_spec_decode_expand.cu_seqlens_q,
|
||||
cu_seqlens_k_new=self.forward_metadata_spec_decode_expand.cu_seqlens_k,
|
||||
max_seqlen_q=self.forward_metadata_spec_decode_expand.max_seq_len_q,
|
||||
softmax_scale=layer.scaling,
|
||||
causal=False,
|
||||
window_size=window_size,
|
||||
softcap=layer.logit_cap,
|
||||
return_softmax_lse=True,
|
||||
num_splits=self.num_splits,
|
||||
**kwargs,
|
||||
)
|
||||
)
|
||||
o, _ = merge_state_v2_wrapper(
|
||||
o,
|
||||
@@ -1888,7 +1884,7 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
assert chunk_idx >= 0
|
||||
|
||||
assert forward_batch.mha_return_lse
|
||||
output = flash_attn_varlen_func(
|
||||
output = self.flash_attn_varlen_func(
|
||||
q=q.view(-1, layer.tp_q_head_num, layer.head_dim),
|
||||
k=k.view(-1, layer.tp_k_head_num, layer.head_dim).to(q.dtype),
|
||||
v=v.view(-1, layer.tp_k_head_num, layer.v_head_dim).to(q.dtype),
|
||||
@@ -1900,7 +1896,6 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
causal=False,
|
||||
return_softmax_lse=True,
|
||||
out=_fa_out,
|
||||
ver=self.fa_impl_ver,
|
||||
**kwargs,
|
||||
)
|
||||
else:
|
||||
@@ -1915,7 +1910,7 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
if not forward_batch.mha_one_shot
|
||||
else metadata.max_seq_len_k
|
||||
)
|
||||
output = flash_attn_varlen_func(
|
||||
output = self.flash_attn_varlen_func(
|
||||
q=q.view(-1, layer.tp_q_head_num, layer.head_dim),
|
||||
k=k.view(-1, layer.tp_k_head_num, layer.head_dim).to(q.dtype),
|
||||
v=v.view(-1, layer.tp_k_head_num, layer.v_head_dim).to(q.dtype),
|
||||
@@ -1927,7 +1922,6 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
causal=True,
|
||||
return_softmax_lse=forward_batch.mha_return_lse,
|
||||
out=_fa_out,
|
||||
ver=self.fa_impl_ver,
|
||||
**kwargs,
|
||||
)
|
||||
if forward_batch.mha_return_lse:
|
||||
@@ -1978,7 +1972,7 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
):
|
||||
q_nope_chunk = q_chunk[..., : layer.v_head_dim]
|
||||
q_rope_chunk = q_chunk[..., layer.v_head_dim :]
|
||||
return flash_attn_with_kvcache(
|
||||
return self.flash_attn_with_kvcache(
|
||||
q=q_rope_chunk,
|
||||
qv=q_nope_chunk,
|
||||
k_cache=k_rope_cache,
|
||||
@@ -1996,7 +1990,6 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
k_descale=fa_k_descale,
|
||||
v_descale=fa_v_descale,
|
||||
num_splits=self.num_splits,
|
||||
ver=self.fa_impl_ver,
|
||||
)
|
||||
|
||||
cp_strategy = get_cp_strategy()
|
||||
@@ -2009,7 +2002,7 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
attention_backend=CPAttentionBackendKind.FLASH_ATTENTION,
|
||||
)
|
||||
else:
|
||||
result = flash_attn_with_kvcache(
|
||||
result = self.flash_attn_with_kvcache(
|
||||
q=q_rope,
|
||||
k_cache=k_rope_cache,
|
||||
v_cache=c_kv_cache,
|
||||
@@ -2026,12 +2019,11 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
v_descale=fa_v_descale,
|
||||
return_softmax_lse=use_cascade_attn,
|
||||
num_splits=self.num_splits,
|
||||
ver=self.fa_impl_ver,
|
||||
)
|
||||
if use_cascade_attn:
|
||||
o, softmax_lse, *rest = result
|
||||
o_expand, softmax_lse_expand, *rest_expand = (
|
||||
flash_attn_with_kvcache(
|
||||
self.flash_attn_with_kvcache(
|
||||
q=q_rope,
|
||||
k_cache=k_rope_cache,
|
||||
v_cache=c_kv_cache,
|
||||
@@ -2049,7 +2041,6 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
v_descale=fa_v_descale,
|
||||
return_softmax_lse=True,
|
||||
num_splits=self.num_splits,
|
||||
ver=self.fa_impl_ver,
|
||||
)
|
||||
)
|
||||
o, _ = merge_state_v2_wrapper(
|
||||
@@ -2206,7 +2197,7 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
# Always use non-chunked logic for cross-attention
|
||||
if self._decode_uses_static_max_seqlen_k:
|
||||
kwargs["max_seqlen_k"] = metadata.encoder_max_seq_len_k
|
||||
o = flash_attn_with_kvcache(
|
||||
o = self.flash_attn_with_kvcache(
|
||||
q=q.contiguous().view(-1, layer.tp_q_head_num, layer.head_dim),
|
||||
k_cache=key_cache,
|
||||
v_cache=value_cache,
|
||||
@@ -2220,14 +2211,13 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
window_size=(-1, -1),
|
||||
softcap=layer.logit_cap,
|
||||
num_splits=self.num_splits,
|
||||
ver=self.fa_impl_ver,
|
||||
**kwargs,
|
||||
)
|
||||
elif use_local_attn:
|
||||
# Use chunked (local) attention batching for self-attention
|
||||
if self._decode_uses_static_max_seqlen_k:
|
||||
kwargs["max_seqlen_k"] = local_attn_metadata.local_max_seq_len
|
||||
o = flash_attn_with_kvcache(
|
||||
o = self.flash_attn_with_kvcache(
|
||||
q=q.contiguous().view(-1, layer.tp_q_head_num, layer.head_dim),
|
||||
k_cache=key_cache,
|
||||
v_cache=value_cache,
|
||||
@@ -2241,7 +2231,6 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
window_size=(-1, -1),
|
||||
softcap=layer.logit_cap,
|
||||
num_splits=self.num_splits,
|
||||
ver=self.fa_impl_ver,
|
||||
**kwargs,
|
||||
)
|
||||
else:
|
||||
@@ -2282,7 +2271,7 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
sched_meta = metadata.scheduler_metadata
|
||||
if self._decode_uses_static_max_seqlen_k:
|
||||
kwargs["max_seqlen_k"] = metadata.max_seq_len_k
|
||||
result = flash_attn_with_kvcache(
|
||||
result = self.flash_attn_with_kvcache(
|
||||
q=q_reshaped,
|
||||
k_cache=key_cache,
|
||||
v_cache=value_cache,
|
||||
@@ -2303,7 +2292,6 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
else self.num_splits
|
||||
),
|
||||
out=_fa_out,
|
||||
ver=self.fa_impl_ver,
|
||||
scheduler_metadata=sched_meta,
|
||||
**kwargs,
|
||||
)
|
||||
@@ -2318,7 +2306,7 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
self.forward_metadata_spec_decode_expand.max_seq_len_k
|
||||
)
|
||||
o_expand, softmax_lse_expand, *rest_expand = (
|
||||
flash_attn_with_kvcache(
|
||||
self.flash_attn_with_kvcache(
|
||||
q=q_reshaped,
|
||||
k_cache=key_cache,
|
||||
v_cache=value_cache,
|
||||
@@ -2333,7 +2321,6 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
softcap=layer.logit_cap,
|
||||
return_softmax_lse=True,
|
||||
num_splits=self.num_splits,
|
||||
ver=self.fa_impl_ver,
|
||||
**kwargs,
|
||||
)
|
||||
)
|
||||
@@ -2363,7 +2350,7 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
q_nope = q_all[:, :, : layer.v_head_dim]
|
||||
q_rope = q_all[:, :, layer.v_head_dim :]
|
||||
max_seqlen_q = metadata.max_seq_len_q
|
||||
result = flash_attn_with_kvcache(
|
||||
result = self.flash_attn_with_kvcache(
|
||||
q=q_rope,
|
||||
k_cache=k_rope_cache,
|
||||
v_cache=c_kv_cache,
|
||||
@@ -2380,29 +2367,29 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
v_descale=fa_v_descale,
|
||||
return_softmax_lse=use_cascade_attn, # softmax_lse is needed for merge states
|
||||
num_splits=self.num_splits,
|
||||
ver=self.fa_impl_ver,
|
||||
)
|
||||
if use_cascade_attn:
|
||||
o, softmax_lse, *rest = result
|
||||
o_expand, softmax_lse_expand, *rest_expand = flash_attn_with_kvcache(
|
||||
q=q_rope,
|
||||
k_cache=k_rope_cache,
|
||||
v_cache=c_kv_cache,
|
||||
qv=q_nope,
|
||||
page_table=self.forward_metadata_spec_decode_expand.page_table,
|
||||
cache_seqlens=self.forward_metadata_spec_decode_expand.cache_seqlens_int32,
|
||||
cu_seqlens_q=self.forward_metadata_spec_decode_expand.cu_seqlens_q,
|
||||
cu_seqlens_k_new=self.forward_metadata_spec_decode_expand.cu_seqlens_k,
|
||||
max_seqlen_q=self.forward_metadata_spec_decode_expand.max_seq_len_q,
|
||||
softmax_scale=layer.scaling,
|
||||
causal=False,
|
||||
window_size=window_size,
|
||||
softcap=layer.logit_cap,
|
||||
k_descale=fa_k_descale,
|
||||
v_descale=fa_v_descale,
|
||||
return_softmax_lse=True,
|
||||
num_splits=self.num_splits,
|
||||
ver=self.fa_impl_ver,
|
||||
o_expand, softmax_lse_expand, *rest_expand = (
|
||||
self.flash_attn_with_kvcache(
|
||||
q=q_rope,
|
||||
k_cache=k_rope_cache,
|
||||
v_cache=c_kv_cache,
|
||||
qv=q_nope,
|
||||
page_table=self.forward_metadata_spec_decode_expand.page_table,
|
||||
cache_seqlens=self.forward_metadata_spec_decode_expand.cache_seqlens_int32,
|
||||
cu_seqlens_q=self.forward_metadata_spec_decode_expand.cu_seqlens_q,
|
||||
cu_seqlens_k_new=self.forward_metadata_spec_decode_expand.cu_seqlens_k,
|
||||
max_seqlen_q=self.forward_metadata_spec_decode_expand.max_seq_len_q,
|
||||
softmax_scale=layer.scaling,
|
||||
causal=False,
|
||||
window_size=window_size,
|
||||
softcap=layer.logit_cap,
|
||||
k_descale=fa_k_descale,
|
||||
v_descale=fa_v_descale,
|
||||
return_softmax_lse=True,
|
||||
num_splits=self.num_splits,
|
||||
)
|
||||
)
|
||||
o, _ = merge_state_v2(
|
||||
o,
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import ast
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
import sglang
|
||||
from sglang.test.ci.ci_register import register_cpu_ci
|
||||
from sglang.test.test_utils import CustomTestCase
|
||||
|
||||
register_cpu_ci(est_time=5, suite="base-a-test-cpu")
|
||||
|
||||
_BACKEND_SOURCE = (
|
||||
Path(sglang.__file__).parent / "srt/layers/attention/flashattention_backend.py"
|
||||
)
|
||||
_ENTRY_POINTS = ("flash_attn_with_kvcache", "flash_attn_varlen_func")
|
||||
|
||||
|
||||
class TestFlashAttentionKernelDispatch(CustomTestCase):
|
||||
def test_kernel_calls_use_selected_entry_points(self):
|
||||
# A call through any name other than self.flash_attn_* bypasses the build
|
||||
# __init__ selected, e.g. generic FA4 on SM12x, or FA3 when `ver` is omitted.
|
||||
tree = ast.parse(_BACKEND_SOURCE.read_text())
|
||||
bypasses = []
|
||||
for node in ast.walk(tree):
|
||||
if not isinstance(node, ast.Call):
|
||||
continue
|
||||
func = node.func
|
||||
if isinstance(func, ast.Name) and func.id in _ENTRY_POINTS:
|
||||
bypasses.append(f"line {node.lineno}: {func.id}(...)")
|
||||
elif (
|
||||
isinstance(func, ast.Attribute)
|
||||
and func.attr in _ENTRY_POINTS
|
||||
and not (isinstance(func.value, ast.Name) and func.value.id == "self")
|
||||
):
|
||||
bypasses.append(f"line {node.lineno}: {ast.unparse(func)}(...)")
|
||||
self.assertEqual(bypasses, [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user