Build the unified read stream directly, without the page-table rectangle (#37512)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
18d5ffb42a
commit
d9848b9ecd
@@ -192,7 +192,6 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
self.needs_cpu_seq_lens = False
|
||||
self.use_mla = model_runner.model_config.attention_arch == AttentionArch.MLA
|
||||
self.kv_index_translator = model_runner.kv_index_translator
|
||||
self.kv_read_tables = None
|
||||
self.skip_prefill = skip_prefill
|
||||
self.attn_cp_size = model_runner.ps.attn_cp_size
|
||||
self._verify_mask = None
|
||||
@@ -2166,12 +2165,6 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
"""
|
||||
max_num_pages = (self.max_context_len + self.page_size - 1) // self.page_size
|
||||
|
||||
if self.kv_index_translator.is_translating:
|
||||
# Zero-filled: slot 0 is the reserved sink in every id space.
|
||||
self.kv_read_tables = self.kv_index_translator.make_capture_tables(
|
||||
max_bs=max_bs, max_context_len=self.max_context_len
|
||||
)
|
||||
|
||||
# This is being used by normal decode and draft decode when topk == 1
|
||||
self.decode_cuda_graph_metadata = {
|
||||
"cache_seqlens": torch.zeros(max_bs, dtype=torch.int32, device=self.device),
|
||||
@@ -2713,6 +2706,44 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
src = seq_lens_cpu if seq_lens_cpu is not None else seq_lens.cpu()
|
||||
return src.max().item()
|
||||
|
||||
def _set_decode_page_metadata(
|
||||
self,
|
||||
metadata,
|
||||
req_pool_indices: torch.Tensor,
|
||||
seq_lens: torch.Tensor,
|
||||
seq_len_delta: int,
|
||||
) -> None:
|
||||
"""Fill `cache_seqlens_int32`, `cu_seqlens_k` and the page table(s).
|
||||
|
||||
Under the unified pool the translator writes the page tables in place,
|
||||
so the fused kernel is left with the prefix sum alone -- one pass over
|
||||
the rows instead of a translated build plus a verbatim copy of it.
|
||||
"""
|
||||
translated = self.kv_index_translator.reads_are_translated
|
||||
normal_decode_set_metadata(
|
||||
metadata.cache_seqlens_int32,
|
||||
metadata.cu_seqlens_k,
|
||||
metadata.page_table,
|
||||
self.req_to_token,
|
||||
req_pool_indices,
|
||||
self.max_num_pages,
|
||||
seq_lens,
|
||||
seq_len_delta,
|
||||
self.page_size,
|
||||
metadata.swa_page_table,
|
||||
self.token_to_kv_pool if self.use_sliding_window_kv_pool else None,
|
||||
skip_page_table=translated,
|
||||
)
|
||||
if translated:
|
||||
# Fill to `cache_seqlens_int32`, which the kernels bound their reads
|
||||
# by: a draft decode reads `seq_len_delta` past `seq_lens`.
|
||||
self.kv_index_translator.fill_read_table(
|
||||
out=metadata.page_table,
|
||||
sliding_window_out=metadata.swa_page_table,
|
||||
req_pool_indices=req_pool_indices,
|
||||
seq_lens=metadata.cache_seqlens_int32,
|
||||
)
|
||||
|
||||
def _apply_cuda_graph_metadata(
|
||||
self,
|
||||
bs: int,
|
||||
@@ -2766,29 +2797,11 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
# is normal-decode-only).
|
||||
# Spec is asserted off under the unified pool, so this
|
||||
# captured view is always the passthrough (req_to_token).
|
||||
kv_view = self.kv_index_translator.build_index_table(
|
||||
req_pool_indices=req_pool_indices,
|
||||
seq_lens=seq_lens,
|
||||
into=self.kv_read_tables,
|
||||
)
|
||||
normal_decode_set_metadata(
|
||||
metadata.cache_seqlens_int32,
|
||||
metadata.cu_seqlens_k,
|
||||
metadata.page_table,
|
||||
kv_view.ids,
|
||||
kv_view.row_ids,
|
||||
self.max_num_pages,
|
||||
self._set_decode_page_metadata(
|
||||
metadata,
|
||||
req_pool_indices,
|
||||
seq_lens,
|
||||
self.speculative_step_id + 1,
|
||||
self.page_size,
|
||||
metadata.swa_page_table,
|
||||
(
|
||||
self.token_to_kv_pool
|
||||
if self.use_sliding_window_kv_pool
|
||||
else None
|
||||
),
|
||||
src_is_read_table=kv_view.is_translated,
|
||||
swa_src_table=kv_view.sliding_window_ids,
|
||||
)
|
||||
|
||||
else:
|
||||
@@ -2888,29 +2901,8 @@ class FlashAttentionBackend(AttentionBackend):
|
||||
if seq_lens_cpu is not None
|
||||
else self.max_context_len
|
||||
)
|
||||
kv_view = self.kv_index_translator.build_index_table(
|
||||
req_pool_indices=req_pool_indices,
|
||||
seq_lens=seq_lens,
|
||||
into=self.kv_read_tables,
|
||||
)
|
||||
normal_decode_set_metadata(
|
||||
metadata.cache_seqlens_int32,
|
||||
metadata.cu_seqlens_k,
|
||||
metadata.page_table,
|
||||
kv_view.ids,
|
||||
kv_view.row_ids,
|
||||
self.max_num_pages,
|
||||
seq_lens,
|
||||
0,
|
||||
self.page_size,
|
||||
metadata.swa_page_table,
|
||||
(
|
||||
self.token_to_kv_pool
|
||||
if self.use_sliding_window_kv_pool
|
||||
else None
|
||||
),
|
||||
src_is_read_table=kv_view.is_translated,
|
||||
swa_src_table=kv_view.sliding_window_ids,
|
||||
self._set_decode_page_metadata(
|
||||
metadata, req_pool_indices, seq_lens, 0
|
||||
)
|
||||
|
||||
self._maybe_update_local_attn_metadata_for_replay(
|
||||
|
||||
@@ -26,7 +26,6 @@ import torch
|
||||
from sglang.kernels.kernel_api_logging import debug_kernel_api
|
||||
from sglang.kernels.ops.attention.utils import (
|
||||
assert_buffer_fits,
|
||||
create_flashinfer_kv_indices_triton,
|
||||
)
|
||||
from sglang.srt.dllm.config import DllmConfig
|
||||
from sglang.srt.environ import envs
|
||||
@@ -36,7 +35,6 @@ from sglang.srt.layers.quantization.fp4_kv_cache_quant_method import (
|
||||
)
|
||||
from sglang.srt.layers.radix_attention import AttentionType
|
||||
from sglang.srt.mem_cache.base_swa_memory_pool import BaseSWAKVPool
|
||||
from sglang.srt.mem_cache.kv_index_translator import KVIndexTable
|
||||
from sglang.srt.mem_cache.memory_pool import KVWriteLoc
|
||||
from sglang.srt.model_executor.cuda_graph_config import (
|
||||
Backend,
|
||||
@@ -312,7 +310,6 @@ class FlashInferAttnBackend(AttentionBackend):
|
||||
self.req_to_token_pool = model_runner.req_to_token_pool
|
||||
self.token_to_kv_pool = model_runner.token_to_kv_pool
|
||||
self.kv_index_translator = model_runner.kv_index_translator
|
||||
self.kv_read_tables = None
|
||||
self._swa_kv_pool: Optional[BaseSWAKVPool] = self._resolve_swa_kv_pool(
|
||||
model_runner
|
||||
)
|
||||
@@ -724,14 +721,6 @@ class FlashInferAttnBackend(AttentionBackend):
|
||||
num_tokens = forward_batch.positions.numel()
|
||||
self._prepare_cuda_graph_metadata(bs, num_tokens, forward_mode, spec_info)
|
||||
|
||||
# All flashinfer gathers run OUT-of-graph (plan time), so the
|
||||
# capture-stable read table is buffer reuse, not pointer stability.
|
||||
kv_view = self.kv_index_translator.build_index_table(
|
||||
req_pool_indices=req_pool_indices[:bs],
|
||||
seq_lens=seq_lens[:bs],
|
||||
into=self.kv_read_tables,
|
||||
)
|
||||
|
||||
if forward_mode.is_decode_or_idle():
|
||||
self.indices_updater_decode.update(
|
||||
seq_lens[:bs],
|
||||
@@ -742,7 +731,7 @@ class FlashInferAttnBackend(AttentionBackend):
|
||||
spec_info=spec_info,
|
||||
fixed_split_size=None,
|
||||
disable_split_kv=self.disable_cuda_graph_kv_split,
|
||||
kv_view=kv_view,
|
||||
req_pool_indices=req_pool_indices,
|
||||
)
|
||||
elif forward_mode.is_target_verify():
|
||||
self.indices_updater_prefill.update(
|
||||
@@ -755,7 +744,6 @@ class FlashInferAttnBackend(AttentionBackend):
|
||||
use_ragged=False,
|
||||
encoder_lens=encoder_lens[:bs] if encoder_lens is not None else None,
|
||||
spec_info=spec_info,
|
||||
kv_view=kv_view,
|
||||
)
|
||||
elif forward_mode.is_dllm_extend():
|
||||
self.indices_updater_prefill.update(
|
||||
@@ -768,7 +756,6 @@ class FlashInferAttnBackend(AttentionBackend):
|
||||
use_ragged=not self.use_paged,
|
||||
encoder_lens=encoder_lens[:bs] if encoder_lens is not None else None,
|
||||
spec_info=None,
|
||||
kv_view=kv_view,
|
||||
)
|
||||
elif forward_mode.is_draft_extend_v2():
|
||||
self.indices_updater_prefill.update(
|
||||
@@ -781,7 +768,6 @@ class FlashInferAttnBackend(AttentionBackend):
|
||||
use_ragged=False,
|
||||
encoder_lens=encoder_lens[:bs] if encoder_lens is not None else None,
|
||||
spec_info=spec_info,
|
||||
kv_view=kv_view,
|
||||
)
|
||||
elif forward_mode.is_extend():
|
||||
# Plain EXTEND under full prefill CUDA graph. plan() runs
|
||||
@@ -800,7 +786,6 @@ class FlashInferAttnBackend(AttentionBackend):
|
||||
use_ragged=False,
|
||||
encoder_lens=encoder_lens[:bs] if encoder_lens is not None else None,
|
||||
spec_info=None,
|
||||
kv_view=kv_view,
|
||||
)
|
||||
else:
|
||||
raise ValueError("Invalid forward mode")
|
||||
@@ -954,7 +939,6 @@ class FlashInferAttnBackend(AttentionBackend):
|
||||
return layer.k_scale, layer.v_scale
|
||||
|
||||
def init_forward_metadata(self, forward_batch: ForwardBatch):
|
||||
kv_view = self.kv_index_translator.index_table_for_batch(forward_batch)
|
||||
swa_out_cache_loc = None
|
||||
if self.use_sliding_window_kv_pool and forward_batch.out_cache_loc is not None:
|
||||
swa_out_cache_loc = self.kv_index_translator.sliding_window_write_loc_for(
|
||||
@@ -971,7 +955,7 @@ class FlashInferAttnBackend(AttentionBackend):
|
||||
spec_info=forward_batch.spec_info,
|
||||
fixed_split_size=self.decode_split_tile_size,
|
||||
disable_split_kv=False,
|
||||
kv_view=kv_view,
|
||||
req_pool_indices=forward_batch.req_pool_indices,
|
||||
)
|
||||
self.forward_metadata = DecodeMetadata(
|
||||
self.decode_wrappers, swa_out_cache_loc=swa_out_cache_loc
|
||||
@@ -987,7 +971,6 @@ class FlashInferAttnBackend(AttentionBackend):
|
||||
use_ragged=False,
|
||||
encoder_lens=forward_batch.encoder_lens,
|
||||
spec_info=forward_batch.spec_info,
|
||||
kv_view=kv_view,
|
||||
)
|
||||
self.forward_metadata = PrefillMetadata(
|
||||
self.prefill_wrappers_verify,
|
||||
@@ -1041,7 +1024,6 @@ class FlashInferAttnBackend(AttentionBackend):
|
||||
cross_attention_custom_mask=forward_batch.cross_attention_custom_mask,
|
||||
extend_prefix_lens_cpu=forward_batch.extend_prefix_lens_cpu,
|
||||
custom_kv_indices=self.dq_page_table,
|
||||
kv_view=kv_view,
|
||||
)
|
||||
self.forward_metadata = PrefillMetadata(
|
||||
self.prefill_wrappers_paged,
|
||||
@@ -1057,9 +1039,6 @@ class FlashInferAttnBackend(AttentionBackend):
|
||||
max_num_tokens: int,
|
||||
kv_indices_buf: Optional[torch.Tensor] = None,
|
||||
):
|
||||
self.kv_read_tables = self.kv_index_translator.make_capture_tables(
|
||||
max_bs=max_bs, max_context_len=self.max_context_len
|
||||
)
|
||||
if kv_indices_buf is None:
|
||||
cuda_graph_kv_indices = torch.zeros(
|
||||
(max_num_tokens * self.max_context_len,),
|
||||
@@ -1586,7 +1565,7 @@ class FlashInferIndicesUpdaterDecode:
|
||||
fixed_split_size: Optional[int] = None,
|
||||
disable_split_kv: Optional[bool] = None,
|
||||
*,
|
||||
kv_view: KVIndexTable,
|
||||
req_pool_indices: torch.Tensor,
|
||||
):
|
||||
# Keep the signature for type checking. It will be assigned during runtime.
|
||||
raise NotImplementedError()
|
||||
@@ -1602,7 +1581,7 @@ class FlashInferIndicesUpdaterDecode:
|
||||
fixed_split_size: Optional[int] = None,
|
||||
disable_split_kv: Optional[bool] = None,
|
||||
*,
|
||||
kv_view: KVIndexTable,
|
||||
req_pool_indices: torch.Tensor,
|
||||
):
|
||||
decode_wrappers = decode_wrappers or self.decode_wrappers
|
||||
self.call_begin_forward(
|
||||
@@ -1615,7 +1594,7 @@ class FlashInferIndicesUpdaterDecode:
|
||||
seq_lens_cpu,
|
||||
fixed_split_size=fixed_split_size,
|
||||
disable_split_kv=disable_split_kv,
|
||||
kv_view=kv_view,
|
||||
req_pool_indices=req_pool_indices,
|
||||
)
|
||||
|
||||
def update_sliding_window(
|
||||
@@ -1629,7 +1608,7 @@ class FlashInferIndicesUpdaterDecode:
|
||||
fixed_split_size: Optional[int] = None,
|
||||
disable_split_kv: Optional[bool] = None,
|
||||
*,
|
||||
kv_view: KVIndexTable,
|
||||
req_pool_indices: torch.Tensor,
|
||||
):
|
||||
assert self.sliding_window_size is not None
|
||||
for wrapper_id in range(2):
|
||||
@@ -1668,7 +1647,7 @@ class FlashInferIndicesUpdaterDecode:
|
||||
use_sliding_window_kv_pool=use_sliding_window_kv_pool,
|
||||
fixed_split_size=fixed_split_size,
|
||||
disable_split_kv=disable_split_kv,
|
||||
kv_view=kv_view,
|
||||
req_pool_indices=req_pool_indices,
|
||||
)
|
||||
|
||||
def update_cross_attention(
|
||||
@@ -1682,7 +1661,7 @@ class FlashInferIndicesUpdaterDecode:
|
||||
fixed_split_size: Optional[int] = None,
|
||||
disable_split_kv: Optional[bool] = None,
|
||||
*,
|
||||
kv_view: KVIndexTable,
|
||||
req_pool_indices: torch.Tensor,
|
||||
):
|
||||
# Cache encoder_lens on CPU to avoid GPU→CPU transfer per call
|
||||
encoder_lens_cpu = encoder_lens.cpu() if encoder_lens is not None else None
|
||||
@@ -1708,7 +1687,7 @@ class FlashInferIndicesUpdaterDecode:
|
||||
seq_lens_cpu=kv_lens_cpu,
|
||||
fixed_split_size=fixed_split_size,
|
||||
disable_split_kv=disable_split_kv,
|
||||
kv_view=kv_view,
|
||||
req_pool_indices=req_pool_indices,
|
||||
)
|
||||
|
||||
def call_begin_forward(
|
||||
@@ -1724,12 +1703,13 @@ class FlashInferIndicesUpdaterDecode:
|
||||
fixed_split_size: Optional[int] = None,
|
||||
disable_split_kv: Optional[bool] = None,
|
||||
*,
|
||||
kv_view: KVIndexTable,
|
||||
req_pool_indices: torch.Tensor,
|
||||
):
|
||||
# Unified SWA wrapper-0: gather from the swa canonical directly -- its
|
||||
# entries are already swa-side kernel-facing ids, so the in-place
|
||||
# full->swa translate below must not run on top of them.
|
||||
use_swa_source = use_sliding_window_kv_pool and kv_view.is_translated
|
||||
translator = self.attn_backend.kv_index_translator
|
||||
use_swa_source = use_sliding_window_kv_pool and translator.reads_are_translated
|
||||
if spec_info is None or getattr(spec_info, "kv_indptr", None) is None:
|
||||
bs = len(paged_kernel_lens)
|
||||
kv_indptr[1 : bs + 1] = torch.cumsum(paged_kernel_lens, dim=0)
|
||||
@@ -1743,20 +1723,14 @@ class FlashInferIndicesUpdaterDecode:
|
||||
paged_kernel_lens_sum, dtype=torch.int32, device="cuda"
|
||||
)
|
||||
|
||||
if use_swa_source:
|
||||
assert kv_view.sliding_window_ids is not None
|
||||
src_table = kv_view.sliding_window_ids
|
||||
else:
|
||||
src_table = kv_view.ids
|
||||
create_flashinfer_kv_indices_triton[(bs,)](
|
||||
src_table,
|
||||
kv_view.row_ids,
|
||||
paged_kernel_lens,
|
||||
kv_indptr,
|
||||
kv_start_idx,
|
||||
kv_indices,
|
||||
kv_view.row_stride,
|
||||
ENTRY_PAGE_SIZE=kv_view.entry_page_size,
|
||||
translator.fill_packed_read_stream(
|
||||
req_pool_indices=req_pool_indices,
|
||||
seq_lens=paged_kernel_lens,
|
||||
indptr=kv_indptr,
|
||||
total_tokens=paged_kernel_lens_sum,
|
||||
out=kv_indices,
|
||||
kv_start_idx=kv_start_idx,
|
||||
sliding_window=use_swa_source,
|
||||
)
|
||||
else:
|
||||
kv_indptr, kv_indices = spec_info.kv_indptr, spec_info.kv_indices
|
||||
@@ -1882,8 +1856,6 @@ class FlashInferIndicesUpdaterPrefill:
|
||||
cross_attention_custom_mask: Optional[torch.Tensor] = None,
|
||||
extend_prefix_lens_cpu: Optional[List[int]] = None,
|
||||
custom_kv_indices: Optional[torch.Tensor] = None,
|
||||
*,
|
||||
kv_view: KVIndexTable,
|
||||
):
|
||||
# Keep the signature for type checking. It will be assigned during runtime.
|
||||
raise NotImplementedError()
|
||||
@@ -1904,8 +1876,6 @@ class FlashInferIndicesUpdaterPrefill:
|
||||
cross_attention_custom_mask: Optional[torch.Tensor] = None,
|
||||
extend_prefix_lens_cpu: Optional[List[int]] = None,
|
||||
custom_kv_indices: Optional[torch.Tensor] = None,
|
||||
*,
|
||||
kv_view: KVIndexTable,
|
||||
):
|
||||
if use_ragged:
|
||||
assert prefix_lens is not None
|
||||
@@ -1936,7 +1906,6 @@ class FlashInferIndicesUpdaterPrefill:
|
||||
multi_item_params=multi_item_params,
|
||||
seq_lens_cpu=seq_lens_cpu,
|
||||
custom_kv_indices=custom_kv_indices,
|
||||
kv_view=kv_view,
|
||||
)
|
||||
|
||||
def update_sliding_window(
|
||||
@@ -1955,8 +1924,6 @@ class FlashInferIndicesUpdaterPrefill:
|
||||
cross_attention_custom_mask: Optional[torch.Tensor] = None,
|
||||
extend_prefix_lens_cpu: Optional[List[int]] = None,
|
||||
custom_kv_indices: Optional[torch.Tensor] = None,
|
||||
*,
|
||||
kv_view: KVIndexTable,
|
||||
):
|
||||
if custom_kv_indices is not None:
|
||||
raise RuntimeError(
|
||||
@@ -2044,7 +2011,6 @@ class FlashInferIndicesUpdaterPrefill:
|
||||
if (wrapper_id == 0 and not use_ragged and spec_info is None)
|
||||
else -1
|
||||
),
|
||||
kv_view=kv_view,
|
||||
)
|
||||
|
||||
def _build_swa_prefix_custom_mask(
|
||||
@@ -2104,8 +2070,6 @@ class FlashInferIndicesUpdaterPrefill:
|
||||
cross_attention_custom_mask: Optional[torch.Tensor] = None,
|
||||
extend_prefix_lens_cpu: Optional[List[int]] = None,
|
||||
custom_kv_indices: Optional[torch.Tensor] = None,
|
||||
*,
|
||||
kv_view: KVIndexTable,
|
||||
):
|
||||
if custom_kv_indices is not None:
|
||||
raise RuntimeError(
|
||||
@@ -2141,7 +2105,6 @@ class FlashInferIndicesUpdaterPrefill:
|
||||
cross_attention_custom_mask=(
|
||||
cross_attention_custom_mask if wrapper_id == 1 else None
|
||||
),
|
||||
kv_view=kv_view,
|
||||
)
|
||||
|
||||
def call_begin_forward(
|
||||
@@ -2165,14 +2128,13 @@ class FlashInferIndicesUpdaterPrefill:
|
||||
seq_lens_cpu: Optional[torch.Tensor] = None,
|
||||
custom_kv_indices: Optional[torch.Tensor] = None,
|
||||
window_left: int = -1,
|
||||
*,
|
||||
kv_view: KVIndexTable,
|
||||
):
|
||||
bs = len(seq_lens)
|
||||
# Unified SWA wrapper-0: gather from the swa canonical directly -- its
|
||||
# entries are already swa-side kernel-facing ids, so the in-place
|
||||
# full->swa translate below must not run on top of them.
|
||||
use_swa_source = use_sliding_window_kv_pool and kv_view.is_translated
|
||||
translator = self.attn_backend.kv_index_translator
|
||||
use_swa_source = use_sliding_window_kv_pool and translator.reads_are_translated
|
||||
if spec_info is None:
|
||||
assert prefix_lens is not None
|
||||
assert len(seq_lens) == len(req_pool_indices)
|
||||
@@ -2198,20 +2160,14 @@ class FlashInferIndicesUpdaterPrefill:
|
||||
dtype=torch.int32,
|
||||
device=req_pool_indices.device,
|
||||
)
|
||||
if use_swa_source:
|
||||
assert kv_view.sliding_window_ids is not None
|
||||
src_table = kv_view.sliding_window_ids
|
||||
else:
|
||||
src_table = kv_view.ids
|
||||
create_flashinfer_kv_indices_triton[(bs,)](
|
||||
src_table,
|
||||
kv_view.row_ids,
|
||||
paged_kernel_lens,
|
||||
kv_indptr,
|
||||
kv_start_idx,
|
||||
kv_indices,
|
||||
kv_view.row_stride,
|
||||
ENTRY_PAGE_SIZE=kv_view.entry_page_size,
|
||||
translator.fill_packed_read_stream(
|
||||
req_pool_indices=req_pool_indices,
|
||||
seq_lens=paged_kernel_lens,
|
||||
indptr=kv_indptr,
|
||||
total_tokens=paged_kernel_lens_sum,
|
||||
out=kv_indices,
|
||||
kv_start_idx=kv_start_idx,
|
||||
sliding_window=use_swa_source,
|
||||
)
|
||||
qo_indptr[1 : bs + 1] = torch.cumsum(seq_lens - prefix_lens, dim=0)
|
||||
qo_indptr = qo_indptr[: bs + 1]
|
||||
|
||||
@@ -27,15 +27,11 @@ import torch
|
||||
from sglang.kernels.ops.attention.utils import assert_buffer_fits
|
||||
from sglang.srt.environ import envs
|
||||
from sglang.srt.layers.attention.base_attn_backend import AttentionBackend
|
||||
from sglang.srt.layers.attention.flashinfer_backend import (
|
||||
create_flashinfer_kv_indices_triton,
|
||||
)
|
||||
from sglang.srt.layers.dcp import (
|
||||
DecodeContextParallelMetadata,
|
||||
update_local_kv_lens_for_dcp,
|
||||
)
|
||||
from sglang.srt.layers.dcp.planner import plan_dcp_decode_metadata
|
||||
from sglang.srt.mem_cache.kv_index_translator import KVIndexTable
|
||||
from sglang.srt.model_executor.forward_batch_info import ForwardBatch, ForwardMode
|
||||
from sglang.srt.model_executor.runner_backend_utils.breakable_cuda_graph import (
|
||||
is_in_breakable_cuda_graph,
|
||||
@@ -239,7 +235,6 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
||||
self.req_to_token_pool = model_runner.req_to_token_pool
|
||||
self.token_to_kv_pool = model_runner.token_to_kv_pool
|
||||
self.kv_index_translator = model_runner.kv_index_translator
|
||||
self.kv_read_tables = None
|
||||
self.enable_chunk_kv = (
|
||||
not skip_prefill
|
||||
and get_disagg().disaggregation_mode != "decode"
|
||||
@@ -344,14 +339,6 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
||||
forward_mode = forward_batch.forward_mode
|
||||
spec_info = forward_batch.spec_info
|
||||
|
||||
# All flashinfer gathers run OUT-of-graph (plan time), so the
|
||||
# capture-stable table is buffer reuse, not pointer stability.
|
||||
kv_view = self.kv_index_translator.build_index_table(
|
||||
req_pool_indices=req_pool_indices[:bs],
|
||||
seq_lens=seq_lens[:bs],
|
||||
into=self.kv_read_tables,
|
||||
)
|
||||
|
||||
if in_capture:
|
||||
num_tokens = forward_batch.positions.numel()
|
||||
seq_lens_sum = seq_lens.sum().item()
|
||||
@@ -373,7 +360,7 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
||||
decode_wrapper=decode_wrapper,
|
||||
init_metadata_replay=False,
|
||||
spec_info=spec_info,
|
||||
kv_view=kv_view,
|
||||
req_pool_indices=req_pool_indices[:bs],
|
||||
)
|
||||
self.decode_cuda_graph_metadata[bs] = decode_wrapper
|
||||
self.forward_metadata = DecodeMetadata(decode_wrapper)
|
||||
@@ -406,7 +393,6 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
||||
spec_info=spec_info,
|
||||
seq_lens_cpu=seq_lens_cpu,
|
||||
in_capture=True,
|
||||
kv_view=kv_view,
|
||||
)
|
||||
if forward_mode.is_target_verify() and (
|
||||
spec_info is None
|
||||
@@ -423,18 +409,16 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
||||
forward_mode=forward_mode,
|
||||
spec_info=spec_info,
|
||||
seq_lens_cpu=forward_batch.seq_lens_cpu,
|
||||
kv_view=kv_view,
|
||||
)
|
||||
|
||||
def init_forward_metadata(self, forward_batch: ForwardBatch):
|
||||
kv_view = self.kv_index_translator.index_table_for_batch(forward_batch)
|
||||
if forward_batch.forward_mode.is_decode_or_idle():
|
||||
self.indices_updater_decode.update(
|
||||
forward_batch.seq_lens,
|
||||
forward_batch.seq_lens_sum,
|
||||
decode_wrapper=self.decode_wrapper,
|
||||
init_metadata_replay=False,
|
||||
kv_view=kv_view,
|
||||
req_pool_indices=forward_batch.req_pool_indices,
|
||||
)
|
||||
self.forward_metadata = DecodeMetadata(self.decode_wrapper)
|
||||
elif forward_batch.forward_mode.is_target_verify():
|
||||
@@ -446,7 +430,6 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
||||
prefill_wrapper_paged=self.prefill_wrapper_verify,
|
||||
use_ragged=False,
|
||||
spec_info=forward_batch.spec_info,
|
||||
kv_view=kv_view,
|
||||
)
|
||||
self.forward_metadata = PrefillMetadata(self.prefill_wrapper_verify, False)
|
||||
else:
|
||||
@@ -495,7 +478,6 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
||||
qo_indptr_cpu=qo_indptr_cpu,
|
||||
kv_indptr_cpu=kv_indptr_cpu,
|
||||
kv_len_arr_cpu=kv_len_arr_cpu,
|
||||
kv_view=kv_view,
|
||||
)
|
||||
self.forward_metadata = PrefillMetadata(
|
||||
self.prefill_wrapper_paged, use_ragged
|
||||
@@ -507,9 +489,6 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
||||
max_num_tokens: int,
|
||||
kv_indices_buf: Optional[torch.Tensor] = None,
|
||||
):
|
||||
self.kv_read_tables = self.kv_index_translator.make_capture_tables(
|
||||
max_bs=max_bs, max_context_len=self.max_context_len
|
||||
)
|
||||
if kv_indices_buf is None:
|
||||
cuda_graph_kv_indices = torch.zeros(
|
||||
(max_bs * self.max_context_len,),
|
||||
@@ -553,7 +532,6 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
||||
forward_mode: ForwardMode,
|
||||
spec_info: Optional[SpecInput],
|
||||
seq_lens_cpu: Optional[torch.Tensor],
|
||||
kv_view: KVIndexTable,
|
||||
in_capture: bool = False,
|
||||
):
|
||||
"""Shared capture+replay body for the cuda-graph init path.
|
||||
@@ -581,7 +559,7 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
||||
decode_wrapper=self.decode_cuda_graph_metadata[bs],
|
||||
init_metadata_replay=True,
|
||||
spec_info=spec_info,
|
||||
kv_view=kv_view,
|
||||
req_pool_indices=req_pool_indices[:bs],
|
||||
**self.fast_decode_kwargs,
|
||||
)
|
||||
elif forward_mode.is_target_verify():
|
||||
@@ -632,7 +610,6 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
||||
if use_generic_fast_plan
|
||||
else None
|
||||
),
|
||||
kv_view=kv_view,
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Invalid forward mode: {forward_mode=}")
|
||||
@@ -875,7 +852,7 @@ class FlashInferMLAIndicesUpdaterDecode:
|
||||
init_metadata_replay: bool = False,
|
||||
spec_info: Optional[SpecInput] = None,
|
||||
*,
|
||||
kv_view: KVIndexTable,
|
||||
req_pool_indices: torch.Tensor,
|
||||
**fast_decode_kwargs,
|
||||
):
|
||||
decode_wrapper = decode_wrapper or self.decode_wrapper
|
||||
@@ -887,7 +864,7 @@ class FlashInferMLAIndicesUpdaterDecode:
|
||||
self.kv_indptr,
|
||||
init_metadata_replay,
|
||||
spec_info,
|
||||
kv_view=kv_view,
|
||||
req_pool_indices=req_pool_indices,
|
||||
**fast_decode_kwargs,
|
||||
)
|
||||
|
||||
@@ -901,7 +878,7 @@ class FlashInferMLAIndicesUpdaterDecode:
|
||||
init_metadata_replay: bool = False,
|
||||
spec_info: Optional[SpecInput] = None,
|
||||
*,
|
||||
kv_view: KVIndexTable,
|
||||
req_pool_indices: torch.Tensor,
|
||||
**fast_decode_kwargs,
|
||||
):
|
||||
bs = len(paged_kernel_lens)
|
||||
@@ -919,18 +896,16 @@ class FlashInferMLAIndicesUpdaterDecode:
|
||||
if not init_metadata_replay
|
||||
else fast_decode_kwargs["kv_indices"]
|
||||
)
|
||||
create_flashinfer_kv_indices_triton[(bs,)](
|
||||
kv_view.ids,
|
||||
kv_view.row_ids,
|
||||
paged_kernel_lens,
|
||||
kv_indptr,
|
||||
None,
|
||||
kv_indices,
|
||||
kv_view.row_stride,
|
||||
ENTRY_PAGE_SIZE=kv_view.entry_page_size,
|
||||
translator = self.attn_backend.kv_index_translator
|
||||
is_translated = translator.fill_packed_read_stream(
|
||||
req_pool_indices=req_pool_indices,
|
||||
seq_lens=paged_kernel_lens,
|
||||
indptr=kv_indptr,
|
||||
total_tokens=paged_kernel_lens_sum,
|
||||
out=kv_indices,
|
||||
)
|
||||
|
||||
# The table above is deliberately VIRTUAL under DCP.
|
||||
# The stream above is deliberately VIRTUAL under DCP.
|
||||
n_kernel_ids = paged_kernel_lens_sum
|
||||
if get_parallel().dcp_enabled:
|
||||
n_kernel_ids = plan_dcp_decode_metadata(
|
||||
@@ -945,9 +920,8 @@ class FlashInferMLAIndicesUpdaterDecode:
|
||||
# capture-stable buffer the captured wrapper reads, so rebinding the
|
||||
# local name would leave the graph on virtual ids. Only the prefix
|
||||
# just filled is translated; the stale tail never indexes v2p.
|
||||
translator = self.attn_backend.kv_index_translator
|
||||
if (
|
||||
not kv_view.is_translated
|
||||
not is_translated
|
||||
and n_kernel_ids > 0
|
||||
and translator.needs_read_translate
|
||||
):
|
||||
@@ -1008,7 +982,7 @@ class FlashInferMLAIndicesUpdaterPrefill:
|
||||
self.qo_indptr = attn_backend.qo_indptr
|
||||
# Kept ONLY for the spec-info branch (generate_attn_arg_prefill), which
|
||||
# is static-pool-only: unified memory asserts spec off. The normal
|
||||
# builder sources from the per-batch KVIndexTable.
|
||||
# builder reads req_to_token through the translator.
|
||||
self.req_to_token = model_runner.req_to_token_pool.req_to_token
|
||||
self.prefill_wrapper_ragged = attn_backend.prefill_wrapper_ragged
|
||||
|
||||
@@ -1024,7 +998,6 @@ class FlashInferMLAIndicesUpdaterPrefill:
|
||||
attn_dcp_metadata: Optional[DecodeContextParallelMetadata] = None,
|
||||
fast_verify_plan_kwargs: Optional[dict] = None,
|
||||
*,
|
||||
kv_view: KVIndexTable,
|
||||
qo_indptr_cpu: Optional[torch.Tensor] = None,
|
||||
kv_indptr_cpu: Optional[torch.Tensor] = None,
|
||||
kv_len_arr_cpu: Optional[torch.Tensor] = None,
|
||||
@@ -1053,7 +1026,6 @@ class FlashInferMLAIndicesUpdaterPrefill:
|
||||
qo_indptr_cpu=qo_indptr_cpu,
|
||||
kv_indptr_cpu=kv_indptr_cpu,
|
||||
kv_len_arr_cpu=kv_len_arr_cpu,
|
||||
kv_view=kv_view,
|
||||
)
|
||||
|
||||
def call_begin_forward(
|
||||
@@ -1072,7 +1044,6 @@ class FlashInferMLAIndicesUpdaterPrefill:
|
||||
attn_dcp_metadata: Optional[DecodeContextParallelMetadata] = None,
|
||||
fast_verify_plan_kwargs: Optional[dict] = None,
|
||||
*,
|
||||
kv_view: KVIndexTable,
|
||||
qo_indptr_cpu: Optional[torch.Tensor] = None,
|
||||
kv_indptr_cpu: Optional[torch.Tensor] = None,
|
||||
kv_len_arr_cpu: Optional[torch.Tensor] = None,
|
||||
@@ -1089,15 +1060,12 @@ class FlashInferMLAIndicesUpdaterPrefill:
|
||||
dtype=torch.int32,
|
||||
device=req_pool_indices.device,
|
||||
)
|
||||
create_flashinfer_kv_indices_triton[(bs,)](
|
||||
kv_view.ids,
|
||||
kv_view.row_ids,
|
||||
paged_kernel_lens,
|
||||
kv_indptr,
|
||||
None,
|
||||
kv_indices,
|
||||
kv_view.row_stride,
|
||||
ENTRY_PAGE_SIZE=kv_view.entry_page_size,
|
||||
self.attn_backend.kv_index_translator.fill_packed_read_stream(
|
||||
req_pool_indices=req_pool_indices,
|
||||
seq_lens=paged_kernel_lens,
|
||||
indptr=kv_indptr,
|
||||
total_tokens=paged_kernel_lens_sum,
|
||||
out=kv_indices,
|
||||
)
|
||||
qo_indptr[1 : bs + 1] = torch.cumsum(seq_lens - prefix_lens, dim=0)
|
||||
qo_indptr = qo_indptr[: bs + 1]
|
||||
|
||||
@@ -7,9 +7,6 @@ import torch
|
||||
import triton
|
||||
|
||||
from sglang.kernels.ops.attention.metadata import get_num_kv_splits_triton
|
||||
from sglang.kernels.ops.kvcache.kv_indices import (
|
||||
create_flashinfer_kv_indices_triton,
|
||||
)
|
||||
from sglang.srt.configs.hybrid_arch import mambaish_config
|
||||
from sglang.srt.configs.model_config import (
|
||||
AttentionArch,
|
||||
@@ -210,7 +207,6 @@ class TritonAttnBackend(AttentionBackend):
|
||||
# byte-identical to the slot-based envelope.
|
||||
self.page_size = getattr(model_runner, "page_size", 1) or 1
|
||||
self.kv_index_translator = model_runner.kv_index_translator
|
||||
self.kv_read_tables = None
|
||||
self.num_draft_tokens = get_spec().speculative_num_draft_tokens
|
||||
self.speculative_num_steps = get_spec().speculative_num_steps
|
||||
self.topk = get_spec().speculative_eagle_topk or 0
|
||||
@@ -461,20 +457,17 @@ class TritonAttnBackend(AttentionBackend):
|
||||
self,
|
||||
bs: int,
|
||||
seq_lens: torch.Tensor,
|
||||
index_table,
|
||||
req_pool_indices: torch.Tensor,
|
||||
kv_indices: torch.Tensor,
|
||||
) -> torch.Tensor:
|
||||
kv_indptr = self.kv_indptr[: bs + 1]
|
||||
kv_indptr[1:] = torch.cumsum(seq_lens, dim=0)
|
||||
create_flashinfer_kv_indices_triton[(bs,)](
|
||||
index_table.ids,
|
||||
index_table.row_ids,
|
||||
seq_lens,
|
||||
kv_indptr,
|
||||
None,
|
||||
kv_indices,
|
||||
index_table.row_stride,
|
||||
ENTRY_PAGE_SIZE=index_table.entry_page_size,
|
||||
self.kv_index_translator.fill_packed_read_stream(
|
||||
req_pool_indices=req_pool_indices[:bs],
|
||||
seq_lens=seq_lens[:bs],
|
||||
indptr=kv_indptr,
|
||||
total_tokens=kv_indices.numel(),
|
||||
out=kv_indices,
|
||||
)
|
||||
return kv_indptr
|
||||
|
||||
@@ -483,7 +476,6 @@ class TritonAttnBackend(AttentionBackend):
|
||||
bs: int,
|
||||
seq_lens: torch.Tensor,
|
||||
req_pool_indices: torch.Tensor,
|
||||
index_table,
|
||||
):
|
||||
"""Fill KV (and SWA) cuda-graph buffers for decode/idle mode.
|
||||
|
||||
@@ -492,9 +484,6 @@ class TritonAttnBackend(AttentionBackend):
|
||||
``num_kv_splits_lens`` is the per-request length used to size kv splits
|
||||
(per-DCP-rank length clamped to >=1 when DCP is enabled, full seq_lens
|
||||
otherwise).
|
||||
|
||||
``index_table`` is the captured read-index view: under the unified pool the
|
||||
gathers below read the converted tables.
|
||||
"""
|
||||
seq_lens = seq_lens[:bs]
|
||||
req_pool_indices = req_pool_indices[:bs]
|
||||
@@ -512,7 +501,7 @@ class TritonAttnBackend(AttentionBackend):
|
||||
num_kv_splits_lens = dcp_seq_lens.clamp_min(1)
|
||||
else:
|
||||
kv_indptr = self._fill_kv_indptr_and_indices(
|
||||
bs, seq_lens, index_table, self.cuda_graph_kv_indices
|
||||
bs, seq_lens, req_pool_indices, self.cuda_graph_kv_indices
|
||||
)
|
||||
num_kv_splits_lens = seq_lens
|
||||
window_kv_indptr = self.window_kv_indptr
|
||||
@@ -520,7 +509,8 @@ class TritonAttnBackend(AttentionBackend):
|
||||
if self.sliding_window_size is not None and self.sliding_window_size > 0:
|
||||
window_kv_indptr, _, window_kv_lens, _ = update_sliding_window_buffer(
|
||||
self.window_kv_indptr,
|
||||
index_table,
|
||||
self.kv_index_translator,
|
||||
req_pool_indices,
|
||||
self.sliding_window_size,
|
||||
seq_lens,
|
||||
bs,
|
||||
@@ -534,7 +524,7 @@ class TritonAttnBackend(AttentionBackend):
|
||||
bs: int,
|
||||
seq_lens: torch.Tensor,
|
||||
spec_info,
|
||||
index_table,
|
||||
req_pool_indices: torch.Tensor,
|
||||
):
|
||||
"""Fill all cuda-graph buffers for target_verify mode."""
|
||||
# Prefer the spec_info's per-request query length (DSpark draft propose
|
||||
@@ -554,7 +544,7 @@ class TritonAttnBackend(AttentionBackend):
|
||||
device=self.device,
|
||||
)
|
||||
kv_indptr = self._fill_kv_indptr_and_indices(
|
||||
bs, seq_lens, index_table, self.cuda_graph_kv_indices
|
||||
bs, seq_lens, req_pool_indices, self.cuda_graph_kv_indices
|
||||
)
|
||||
window_kv_indptr = self.window_kv_indptr
|
||||
window_kv_indices = None
|
||||
@@ -567,7 +557,8 @@ class TritonAttnBackend(AttentionBackend):
|
||||
window_kv_indptr, window_kv_indices, _, window_kv_offsets[:bs] = (
|
||||
update_sliding_window_buffer(
|
||||
self.window_kv_indptr,
|
||||
index_table,
|
||||
self.kv_index_translator,
|
||||
req_pool_indices,
|
||||
self.sliding_window_size,
|
||||
seq_lens[:bs],
|
||||
bs,
|
||||
@@ -605,7 +596,7 @@ class TritonAttnBackend(AttentionBackend):
|
||||
seq_lens: torch.Tensor,
|
||||
forward_mode: ForwardMode,
|
||||
spec_info: Optional[SpecInput],
|
||||
index_table,
|
||||
req_pool_indices: torch.Tensor,
|
||||
):
|
||||
"""Fill QO + KV cuda-graph buffers for draft_extend mode."""
|
||||
seq_lens = seq_lens[:bs]
|
||||
@@ -636,7 +627,7 @@ class TritonAttnBackend(AttentionBackend):
|
||||
extend_seq_lens = torch.zeros(bs, dtype=torch.int32, device=seq_lens.device)
|
||||
kv_lens = torch.clamp(seq_lens - extend_seq_lens, min=0).to(torch.int32)
|
||||
kv_indptr = self._fill_kv_indptr_and_indices(
|
||||
bs, kv_lens, index_table, self.cuda_graph_kv_indices
|
||||
bs, kv_lens, req_pool_indices, self.cuda_graph_kv_indices
|
||||
)
|
||||
return qo_indptr, kv_indptr, num_tokens_per_req
|
||||
|
||||
@@ -763,9 +754,6 @@ class TritonAttnBackend(AttentionBackend):
|
||||
|
||||
if forward_batch.forward_mode.is_decode_or_idle():
|
||||
if spec_info is None or spec_info.kv_indptr is None:
|
||||
index_table = self.kv_index_translator.index_table_for_batch(
|
||||
forward_batch
|
||||
)
|
||||
# kv_indptr is None for draft-extend's idle batch; build from seq_lens.
|
||||
if self.dcp_size > 1:
|
||||
# DCP: per-rank sharded KV indices, else each rank reads the
|
||||
@@ -786,7 +774,7 @@ class TritonAttnBackend(AttentionBackend):
|
||||
kv_indptr = self._fill_kv_indptr_and_indices(
|
||||
bs,
|
||||
forward_batch.seq_lens,
|
||||
index_table,
|
||||
forward_batch.req_pool_indices,
|
||||
kv_indices,
|
||||
)
|
||||
if (
|
||||
@@ -796,7 +784,8 @@ class TritonAttnBackend(AttentionBackend):
|
||||
window_kv_indptr, window_kv_indices, window_kv_lens, _ = (
|
||||
update_sliding_window_buffer(
|
||||
self.window_kv_indptr,
|
||||
index_table,
|
||||
self.kv_index_translator,
|
||||
forward_batch.req_pool_indices,
|
||||
self.sliding_window_size,
|
||||
forward_batch.seq_lens,
|
||||
bs,
|
||||
@@ -888,11 +877,10 @@ class TritonAttnBackend(AttentionBackend):
|
||||
kv_indices = torch.empty(
|
||||
seq_lens_sum, dtype=torch.int64, device=self.device
|
||||
)
|
||||
index_table = self.kv_index_translator.index_table_for_batch(forward_batch)
|
||||
kv_indptr = self._fill_kv_indptr_and_indices(
|
||||
bs,
|
||||
forward_batch.seq_lens,
|
||||
index_table,
|
||||
forward_batch.req_pool_indices,
|
||||
kv_indices,
|
||||
)
|
||||
|
||||
@@ -905,7 +893,8 @@ class TritonAttnBackend(AttentionBackend):
|
||||
window_kv_offsets,
|
||||
) = update_sliding_window_buffer(
|
||||
self.window_kv_indptr,
|
||||
index_table,
|
||||
self.kv_index_translator,
|
||||
forward_batch.req_pool_indices,
|
||||
self.sliding_window_size,
|
||||
forward_batch.seq_lens,
|
||||
bs,
|
||||
@@ -926,7 +915,6 @@ class TritonAttnBackend(AttentionBackend):
|
||||
attn_lse = None
|
||||
|
||||
else:
|
||||
index_table = self.kv_index_translator.index_table_for_batch(forward_batch)
|
||||
if self.dcp_size > 1:
|
||||
kv_indptr, kv_indices, _ = self._dcp_kv_indices(
|
||||
forward_batch.req_pool_indices,
|
||||
@@ -947,7 +935,7 @@ class TritonAttnBackend(AttentionBackend):
|
||||
kv_indptr = self._fill_kv_indptr_and_indices(
|
||||
bs,
|
||||
forward_batch.extend_prefix_lens,
|
||||
index_table,
|
||||
forward_batch.req_pool_indices,
|
||||
kv_indices,
|
||||
)
|
||||
if self.sliding_window_size is not None and self.sliding_window_size > 0:
|
||||
@@ -958,7 +946,8 @@ class TritonAttnBackend(AttentionBackend):
|
||||
window_kv_offsets,
|
||||
) = update_sliding_window_buffer(
|
||||
self.window_kv_indptr,
|
||||
index_table,
|
||||
self.kv_index_translator,
|
||||
forward_batch.req_pool_indices,
|
||||
self.sliding_window_size,
|
||||
forward_batch.extend_prefix_lens,
|
||||
bs,
|
||||
@@ -1135,9 +1124,6 @@ class TritonAttnBackend(AttentionBackend):
|
||||
dtype=torch.int64,
|
||||
device=self.device,
|
||||
)
|
||||
self.kv_read_tables = self.kv_index_translator.make_capture_tables(
|
||||
max_bs=max_bs, max_context_len=self.max_context_len
|
||||
)
|
||||
|
||||
def _build_cuda_graph_forward_metadata(
|
||||
self,
|
||||
@@ -1257,15 +1243,10 @@ class TritonAttnBackend(AttentionBackend):
|
||||
Public entry: :py:meth:`init_forward_metadata_out_graph`.
|
||||
"""
|
||||
# NOTE: encoder_lens expected to be zeros or None
|
||||
index_table = self.kv_index_translator.build_index_table(
|
||||
req_pool_indices=req_pool_indices,
|
||||
seq_lens=seq_lens,
|
||||
into=self.kv_read_tables,
|
||||
)
|
||||
if forward_mode.is_decode_or_idle():
|
||||
assert spec_info is None, "Multi-step cuda graph init is not done here."
|
||||
_, _, window_kv_lens, num_kv_splits_lens = self._update_decode_kv_buffers(
|
||||
bs, seq_lens, req_pool_indices, index_table
|
||||
bs, seq_lens, req_pool_indices
|
||||
)
|
||||
self.get_num_kv_splits(
|
||||
self.cuda_graph_num_kv_splits[:bs], num_kv_splits_lens[:bs]
|
||||
@@ -1276,10 +1257,12 @@ class TritonAttnBackend(AttentionBackend):
|
||||
)
|
||||
elif forward_mode.is_target_verify():
|
||||
bs = len(req_pool_indices)
|
||||
self._update_target_verify_buffers(bs, seq_lens, spec_info, index_table)
|
||||
self._update_target_verify_buffers(
|
||||
bs, seq_lens, spec_info, req_pool_indices
|
||||
)
|
||||
elif forward_mode.is_draft_extend_v2():
|
||||
self._update_draft_extend_buffers(
|
||||
bs, seq_lens, forward_mode, spec_info, index_table
|
||||
bs, seq_lens, forward_mode, spec_info, req_pool_indices
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
@@ -2198,7 +2181,8 @@ class TritonMultiStepDraftBackend:
|
||||
|
||||
def update_sliding_window_buffer(
|
||||
window_kv_indptr,
|
||||
index_table,
|
||||
translator,
|
||||
req_pool_indices,
|
||||
sliding_window_size,
|
||||
seq_lens,
|
||||
bs,
|
||||
@@ -2212,12 +2196,11 @@ def update_sliding_window_buffer(
|
||||
path); omit it (or pass ``None``) to allocate a fresh tensor (eager path,
|
||||
requires ``device``).
|
||||
|
||||
``index_table`` is the batch's read-index source view. Unified pool: the
|
||||
gather reads the parallel SWA array (built directly from virtual ids
|
||||
through the swa side's own v2p), so the window indices come out
|
||||
already swa-side ids -- no translate here, eager or captured. Static SWA
|
||||
pools gather full-token ids from req_to_token and keep the legacy
|
||||
full->swa translate below.
|
||||
Unified pool: the gather reads the swa sub-pool's own id space (built
|
||||
directly from virtual ids through the swa side's own v2p), so the window
|
||||
indices come out already swa-side ids -- no translate here, eager or
|
||||
captured. Static SWA pools gather full-token ids from req_to_token and keep
|
||||
the legacy full->swa translate below.
|
||||
"""
|
||||
window_kv_lens = torch.minimum(
|
||||
seq_lens,
|
||||
@@ -2230,18 +2213,16 @@ def update_sliding_window_buffer(
|
||||
window_kv_indptr[-1], dtype=torch.int64, device=device
|
||||
)
|
||||
window_kv_start_idx = seq_lens - window_kv_lens
|
||||
source_ids = index_table.sliding_window_read_ids()
|
||||
create_flashinfer_kv_indices_triton[(bs,)](
|
||||
source_ids,
|
||||
index_table.row_ids,
|
||||
window_kv_lens,
|
||||
window_kv_indptr,
|
||||
window_kv_start_idx,
|
||||
window_kv_indices,
|
||||
source_ids.stride(0),
|
||||
ENTRY_PAGE_SIZE=index_table.entry_page_size,
|
||||
translated = translator.fill_packed_read_stream(
|
||||
req_pool_indices=req_pool_indices[:bs],
|
||||
seq_lens=window_kv_lens,
|
||||
indptr=window_kv_indptr,
|
||||
total_tokens=window_kv_indices.numel(),
|
||||
out=window_kv_indices,
|
||||
kv_start_idx=window_kv_start_idx,
|
||||
sliding_window=translator.reads_are_translated,
|
||||
)
|
||||
if not index_table.is_translated and isinstance(token_to_kv_pool, BaseSWAKVPool):
|
||||
if not translated and isinstance(token_to_kv_pool, BaseSWAKVPool):
|
||||
kv_last_index = window_kv_indptr[-1]
|
||||
window_kv_indices[:kv_last_index] = (
|
||||
token_to_kv_pool.translate_loc_from_full_to_swa(
|
||||
|
||||
@@ -469,9 +469,6 @@ class TRTLLMHAAttnBackend(FlashInferAttnBackend):
|
||||
kv_indices_buf: Optional[torch.Tensor] = None,
|
||||
):
|
||||
"""Initialize CUDA graph state for TRTLLM MHA."""
|
||||
self.kv_read_tables = self.kv_index_translator.make_capture_tables(
|
||||
max_bs=max_bs, max_context_len=self.max_context_len
|
||||
)
|
||||
max_num_pages = self.max_num_pages
|
||||
self.decode_cuda_graph_metadata = {
|
||||
"cache_seqlens": torch.zeros(max_bs, dtype=torch.int32, device=self.device),
|
||||
@@ -898,21 +895,21 @@ class TRTLLMHAAttnBackend(FlashInferAttnBackend):
|
||||
)
|
||||
|
||||
if self.kv_index_translator.is_translating:
|
||||
# Unified pool: refresh the capture-stable read table (this runs
|
||||
# Unified pool: refill this mode's own page table (this runs
|
||||
# out-of-graph on BOTH capture and every replay-prep; the recorded
|
||||
# fused kernel skips its page-table writes so the graph reads the
|
||||
# refreshed content through pointers baked at capture).
|
||||
kv_view = self.kv_index_translator.build_index_table(
|
||||
req_pool_indices=forward_batch.req_pool_indices[:bs],
|
||||
seq_lens=forward_batch.seq_lens[:bs],
|
||||
into=self.kv_read_tables,
|
||||
)
|
||||
metadata = self.forward_metadata
|
||||
if in_capture:
|
||||
# Bind ONCE: the attention kernels bake these pointers at capture.
|
||||
metadata.page_table = kv_view.ids[:bs]
|
||||
if kv_view.sliding_window_ids is not None:
|
||||
metadata.swa_page_table = kv_view.sliding_window_ids[:bs]
|
||||
# `cache_seqlens_int32` is what the attention kernels bound their
|
||||
# page-table reads by, and the fused metadata call above wrote it.
|
||||
# A target verify reads `draft_token_num` further than `seq_lens`
|
||||
# goes, so filling to `seq_lens` leaves those columns untranslated.
|
||||
self.kv_index_translator.fill_read_table(
|
||||
out=metadata.page_table,
|
||||
req_pool_indices=forward_batch.req_pool_indices[:bs],
|
||||
seq_lens=metadata.cache_seqlens_int32,
|
||||
sliding_window_out=metadata.swa_page_table,
|
||||
)
|
||||
# A capture batch carries no prepared write loc; zeros are the
|
||||
# page-0 sink.
|
||||
if (
|
||||
|
||||
@@ -57,7 +57,13 @@ from typing import Optional, Tuple
|
||||
import msgspec
|
||||
import torch
|
||||
|
||||
from sglang.kernels.ops.kvcache.kv_read_table import build_kv_read_table
|
||||
from sglang.kernels.ops.kvcache.kv_indices import (
|
||||
create_flashinfer_kv_indices_triton,
|
||||
)
|
||||
from sglang.kernels.ops.kvcache.kv_read_table import (
|
||||
build_kv_read_table,
|
||||
build_kv_read_table_packed,
|
||||
)
|
||||
from sglang.srt.mem_cache.multi_ended_allocator import (
|
||||
UnifiedMambaTokenToKVPoolAllocator,
|
||||
UnifiedSWATokenToKVPoolAllocator,
|
||||
@@ -161,32 +167,85 @@ class KVIndexTranslator:
|
||||
)
|
||||
self._index_table_memo: Optional[Tuple[weakref.ref, KVIndexTable]] = None
|
||||
|
||||
def make_capture_tables(
|
||||
self, *, max_bs: int, max_context_len: int
|
||||
) -> Optional[KVReadTables]:
|
||||
"""Capture-stable destinations for a backend to own, or None when this
|
||||
pool needs no translation and the backend will never fill any.
|
||||
|
||||
Zero-filled: entry 0 is the reserved padding slot in every id space, so
|
||||
a captured graph replaying before its first refresh reads padding, not
|
||||
garbage.
|
||||
"""
|
||||
if not self.is_translating:
|
||||
return None
|
||||
max_pages = -(-max_context_len // self.page_size)
|
||||
|
||||
def _zeros():
|
||||
return torch.zeros(
|
||||
(max_bs, max_pages), dtype=torch.int32, device=self.device
|
||||
)
|
||||
|
||||
return KVReadTables(
|
||||
full=_zeros(),
|
||||
sliding_window=_zeros() if self._swa_v2p_table is not None else None,
|
||||
)
|
||||
|
||||
# -- per-batch view --------------------------------------------------------
|
||||
|
||||
@property
|
||||
def reads_are_translated(self) -> bool:
|
||||
"""Whether a read this translator fills comes out kernel-facing. False
|
||||
on a non-unified pool, and under DCP, where the ids stay VIRTUAL for
|
||||
``translate_dcp_read_ids`` to finish."""
|
||||
return self.is_translating and not self.defer_read_translate
|
||||
|
||||
def fill_packed_read_stream(
|
||||
self,
|
||||
*,
|
||||
req_pool_indices: torch.Tensor,
|
||||
seq_lens: torch.Tensor,
|
||||
indptr: torch.Tensor,
|
||||
total_tokens: int,
|
||||
out: torch.Tensor,
|
||||
kv_start_idx: Optional[torch.Tensor] = None,
|
||||
sliding_window: bool = False,
|
||||
) -> bool:
|
||||
"""Fill ``out``'s CSR rows with the ids a paged wrapper plans over, and
|
||||
report whether they came out translated.
|
||||
|
||||
Non-unified: the historical gather straight from ``req_to_token``.
|
||||
Unified: one fused gather-and-translate, so no caller needs a
|
||||
``[bs, max_pages]`` rectangle to repack from -- ``out`` holds one id per
|
||||
resident token, a length the pool bounds.
|
||||
|
||||
``sliding_window`` selects the swa sub-pool's own id space, built from
|
||||
VIRTUAL ids and never chained through full-physical. A ``False`` return
|
||||
means the ids are still VIRTUAL: the DCP path defers translation to
|
||||
``translate_dcp_read_ids``, and a static SWA pool maps the full ids
|
||||
through its own full->swa table.
|
||||
"""
|
||||
# `seq_lens` sizes the batch: a caller may hold a wider req_pool_indices
|
||||
# (the padded graph buffer), and the extra lanes have no length to bound.
|
||||
bs = int(seq_lens.numel())
|
||||
assert req_pool_indices.numel() >= bs, (
|
||||
f"fill_packed_read_stream: {req_pool_indices.numel()} req rows for "
|
||||
f"{bs} lengths"
|
||||
)
|
||||
req_pool_indices = req_pool_indices[:bs]
|
||||
|
||||
if not self.reads_are_translated:
|
||||
create_flashinfer_kv_indices_triton[(bs,)](
|
||||
self.req_to_token,
|
||||
req_pool_indices,
|
||||
seq_lens,
|
||||
indptr,
|
||||
kv_start_idx,
|
||||
out,
|
||||
self.req_to_token.stride(0),
|
||||
ENTRY_PAGE_SIZE=1,
|
||||
)
|
||||
return False
|
||||
|
||||
if sliding_window:
|
||||
assert self._swa_v2p_table is not None, (
|
||||
"fill_packed_read_stream: sliding_window on a pool with no swa "
|
||||
"sub-pool"
|
||||
)
|
||||
build_kv_read_table_packed(
|
||||
req_to_token=self.req_to_token,
|
||||
req_pool_indices=req_pool_indices,
|
||||
seq_lens=seq_lens,
|
||||
v2p=self._swa_v2p_table if sliding_window else self._full_v2p_table,
|
||||
indptr=indptr,
|
||||
multiplier=(
|
||||
self._swa_page_multiplier
|
||||
if sliding_window
|
||||
else self._full_page_multiplier
|
||||
),
|
||||
page_size=self.page_size,
|
||||
max_tokens=total_tokens,
|
||||
out=out,
|
||||
kv_start_idx=kv_start_idx,
|
||||
)
|
||||
return True
|
||||
|
||||
def build_index_table(
|
||||
self,
|
||||
*,
|
||||
@@ -273,18 +332,31 @@ class KVIndexTranslator:
|
||||
out: torch.Tensor,
|
||||
req_pool_indices: torch.Tensor,
|
||||
seq_lens: torch.Tensor,
|
||||
sliding_window_out: Optional[torch.Tensor] = None,
|
||||
) -> None:
|
||||
"""`build_index_table(into=...)` for a caller that owns a bare block
|
||||
table rather than a KVReadTables: trtllm_mla / flashmla consume that
|
||||
table rather than a KVReadTables: the page-table consumers read that
|
||||
table directly, its rows already being the index table's rows.
|
||||
|
||||
`sliding_window_out` fills the swa twin in the same pass, for a hybrid
|
||||
model whose kernels take two block tables.
|
||||
"""
|
||||
assert (
|
||||
self.is_translating
|
||||
), "KVIndexTranslator.fill_read_table on a pool that needs no translation"
|
||||
# `reads_are_translated`, not `is_translating`: under DCP the builder
|
||||
# returns the passthrough view and writes nothing, so `is_translating`
|
||||
# would let a caller keep a stale table and never hear about it.
|
||||
assert self.reads_are_translated, (
|
||||
"KVIndexTranslator.fill_read_table cannot fill a page table when "
|
||||
"reads stay virtual (a non-unified pool, or DCP, where the caller "
|
||||
"must select this rank's share itself)"
|
||||
)
|
||||
assert sliding_window_out is None or self._swa_v2p_table is not None, (
|
||||
"KVIndexTranslator.fill_read_table: asked for a sliding-window "
|
||||
"table on a pool with no swa sub-pool"
|
||||
)
|
||||
self.build_index_table(
|
||||
req_pool_indices=req_pool_indices,
|
||||
seq_lens=seq_lens,
|
||||
into=KVReadTables(full=out, sliding_window=None),
|
||||
into=KVReadTables(full=out, sliding_window=sliding_window_out),
|
||||
)
|
||||
|
||||
def index_table_for_batch(self, forward_batch) -> KVIndexTable:
|
||||
|
||||
Reference in New Issue
Block a user