[Perf] FlashInfer MLA: remove blocking D2H in spec-decode plan (#27689)
This commit is contained in:
@@ -314,6 +314,18 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
|||||||
self.decode_cuda_graph_metadata = {}
|
self.decode_cuda_graph_metadata = {}
|
||||||
self.prefill_cuda_graph_metadata = {} # For verify
|
self.prefill_cuda_graph_metadata = {} # For verify
|
||||||
|
|
||||||
|
# Pinned host buffers for the fast prefill path plan
|
||||||
|
if not skip_prefill:
|
||||||
|
self.fast_plan_qo_indptr_cpu = torch.zeros(
|
||||||
|
(max_bs + 1,), dtype=torch.int32, device="cpu", pin_memory=True
|
||||||
|
)
|
||||||
|
self.fast_plan_kv_indptr_cpu = torch.zeros(
|
||||||
|
(max_bs + 1,), dtype=torch.int32, device="cpu", pin_memory=True
|
||||||
|
)
|
||||||
|
self.fast_plan_kv_len_arr_cpu = torch.zeros(
|
||||||
|
(max_bs,), dtype=torch.int32, device="cpu", pin_memory=True
|
||||||
|
)
|
||||||
|
|
||||||
def init_forward_metadata_out_graph(
|
def init_forward_metadata_out_graph(
|
||||||
self,
|
self,
|
||||||
forward_batch: ForwardBatch,
|
forward_batch: ForwardBatch,
|
||||||
@@ -379,6 +391,9 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
|||||||
spec_info=spec_info,
|
spec_info=spec_info,
|
||||||
seq_lens_cpu=seq_lens_cpu,
|
seq_lens_cpu=seq_lens_cpu,
|
||||||
)
|
)
|
||||||
|
if forward_mode.is_target_verify():
|
||||||
|
# use sync-free fast_mla_prefill_plan for replay
|
||||||
|
prefill_wrapper.plan = partial(fast_mla_prefill_plan, prefill_wrapper)
|
||||||
else:
|
else:
|
||||||
self._apply_cuda_graph_metadata(
|
self._apply_cuda_graph_metadata(
|
||||||
bs=bs,
|
bs=bs,
|
||||||
@@ -424,6 +439,28 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
|||||||
and not is_in_breakable_cuda_graph()
|
and not is_in_breakable_cuda_graph()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# build host indptr/len arrays for eager DRAFT_EXTEND_V2 fast plan path
|
||||||
|
qo_indptr_cpu = kv_indptr_cpu = kv_len_arr_cpu = None
|
||||||
|
spec_info = forward_batch.spec_info
|
||||||
|
if (
|
||||||
|
not use_ragged
|
||||||
|
and forward_batch.forward_mode.is_draft_extend_v2()
|
||||||
|
and forward_batch.seq_lens_cpu is not None
|
||||||
|
and spec_info is not None
|
||||||
|
):
|
||||||
|
ndt = spec_info.num_tokens_per_req
|
||||||
|
bs = forward_batch.batch_size
|
||||||
|
self.fast_plan_qo_indptr_cpu[: bs + 1] = torch.arange(
|
||||||
|
0, (bs + 1) * ndt, ndt, dtype=torch.int32
|
||||||
|
)
|
||||||
|
self.fast_plan_kv_len_arr_cpu[:bs] = forward_batch.seq_lens_cpu[:bs]
|
||||||
|
self.fast_plan_kv_indptr_cpu[1 : bs + 1] = torch.cumsum(
|
||||||
|
self.fast_plan_kv_len_arr_cpu[:bs], dim=0
|
||||||
|
)
|
||||||
|
qo_indptr_cpu = self.fast_plan_qo_indptr_cpu[: bs + 1]
|
||||||
|
kv_indptr_cpu = self.fast_plan_kv_indptr_cpu[: bs + 1]
|
||||||
|
kv_len_arr_cpu = self.fast_plan_kv_len_arr_cpu[:bs]
|
||||||
|
|
||||||
self.indices_updater_prefill.update(
|
self.indices_updater_prefill.update(
|
||||||
forward_batch.req_pool_indices,
|
forward_batch.req_pool_indices,
|
||||||
forward_batch.seq_lens,
|
forward_batch.seq_lens,
|
||||||
@@ -432,6 +469,9 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
|||||||
prefill_wrapper_paged=self.prefill_wrapper_paged,
|
prefill_wrapper_paged=self.prefill_wrapper_paged,
|
||||||
use_ragged=use_ragged,
|
use_ragged=use_ragged,
|
||||||
attn_dcp_metadata=forward_batch.attn_dcp_metadata,
|
attn_dcp_metadata=forward_batch.attn_dcp_metadata,
|
||||||
|
qo_indptr_cpu=qo_indptr_cpu,
|
||||||
|
kv_indptr_cpu=kv_indptr_cpu,
|
||||||
|
kv_len_arr_cpu=kv_len_arr_cpu,
|
||||||
)
|
)
|
||||||
self.forward_metadata = PrefillMetadata(
|
self.forward_metadata = PrefillMetadata(
|
||||||
self.prefill_wrapper_paged, use_ragged
|
self.prefill_wrapper_paged, use_ragged
|
||||||
@@ -516,6 +556,18 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
|||||||
**self.fast_decode_kwargs,
|
**self.fast_decode_kwargs,
|
||||||
)
|
)
|
||||||
elif forward_mode.is_target_verify():
|
elif forward_mode.is_target_verify():
|
||||||
|
# build host indptr/len arrays for target-verify fast plan path
|
||||||
|
assert (
|
||||||
|
seq_lens_cpu is not None and spec_info is not None
|
||||||
|
), "target-verify cuda-graph replay requires host-resident seq_lens_cpu"
|
||||||
|
ndt = spec_info.draft_token_num
|
||||||
|
self.fast_plan_qo_indptr_cpu[: bs + 1] = torch.arange(
|
||||||
|
0, (bs + 1) * ndt, ndt, dtype=torch.int32
|
||||||
|
)
|
||||||
|
self.fast_plan_kv_len_arr_cpu[:bs] = seq_lens_cpu[:bs] + ndt
|
||||||
|
self.fast_plan_kv_indptr_cpu[1 : bs + 1] = torch.cumsum(
|
||||||
|
self.fast_plan_kv_len_arr_cpu[:bs], dim=0
|
||||||
|
)
|
||||||
self.indices_updater_prefill.update(
|
self.indices_updater_prefill.update(
|
||||||
req_pool_indices[:bs],
|
req_pool_indices[:bs],
|
||||||
seq_lens[:bs],
|
seq_lens[:bs],
|
||||||
@@ -526,6 +578,9 @@ class FlashInferMLAAttnBackend(AttentionBackend):
|
|||||||
],
|
],
|
||||||
use_ragged=False,
|
use_ragged=False,
|
||||||
spec_info=spec_info,
|
spec_info=spec_info,
|
||||||
|
qo_indptr_cpu=self.fast_plan_qo_indptr_cpu[: bs + 1],
|
||||||
|
kv_indptr_cpu=self.fast_plan_kv_indptr_cpu[: bs + 1],
|
||||||
|
kv_len_arr_cpu=self.fast_plan_kv_len_arr_cpu[:bs],
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Invalid forward mode: {forward_mode=}")
|
raise ValueError(f"Invalid forward mode: {forward_mode=}")
|
||||||
@@ -853,7 +908,7 @@ class FlashInferMLAIndicesUpdaterPrefill:
|
|||||||
|
|
||||||
def update(
|
def update(
|
||||||
self,
|
self,
|
||||||
req_pool_indices: torch.Tnesor,
|
req_pool_indices: torch.Tensor,
|
||||||
seq_lens: torch.Tensor,
|
seq_lens: torch.Tensor,
|
||||||
seq_lens_sum: int,
|
seq_lens_sum: int,
|
||||||
prefix_lens: torch.Tensor,
|
prefix_lens: torch.Tensor,
|
||||||
@@ -861,6 +916,9 @@ class FlashInferMLAIndicesUpdaterPrefill:
|
|||||||
use_ragged: bool,
|
use_ragged: bool,
|
||||||
spec_info: Optional[SpecInput] = None,
|
spec_info: Optional[SpecInput] = None,
|
||||||
attn_dcp_metadata: Optional[DecodeContextParallelMetadata] = None,
|
attn_dcp_metadata: Optional[DecodeContextParallelMetadata] = None,
|
||||||
|
qo_indptr_cpu: Optional[torch.Tensor] = None,
|
||||||
|
kv_indptr_cpu: Optional[torch.Tensor] = None,
|
||||||
|
kv_len_arr_cpu: Optional[torch.Tensor] = None,
|
||||||
):
|
):
|
||||||
if use_ragged:
|
if use_ragged:
|
||||||
paged_kernel_lens = prefix_lens
|
paged_kernel_lens = prefix_lens
|
||||||
@@ -882,6 +940,9 @@ class FlashInferMLAIndicesUpdaterPrefill:
|
|||||||
use_ragged,
|
use_ragged,
|
||||||
spec_info,
|
spec_info,
|
||||||
attn_dcp_metadata=attn_dcp_metadata,
|
attn_dcp_metadata=attn_dcp_metadata,
|
||||||
|
qo_indptr_cpu=qo_indptr_cpu,
|
||||||
|
kv_indptr_cpu=kv_indptr_cpu,
|
||||||
|
kv_len_arr_cpu=kv_len_arr_cpu,
|
||||||
)
|
)
|
||||||
|
|
||||||
def call_begin_forward(
|
def call_begin_forward(
|
||||||
@@ -898,6 +959,9 @@ class FlashInferMLAIndicesUpdaterPrefill:
|
|||||||
use_ragged: bool,
|
use_ragged: bool,
|
||||||
spec_info: Optional[SpecInput] = None,
|
spec_info: Optional[SpecInput] = None,
|
||||||
attn_dcp_metadata: Optional[DecodeContextParallelMetadata] = None,
|
attn_dcp_metadata: Optional[DecodeContextParallelMetadata] = None,
|
||||||
|
qo_indptr_cpu: Optional[torch.Tensor] = None,
|
||||||
|
kv_indptr_cpu: Optional[torch.Tensor] = None,
|
||||||
|
kv_len_arr_cpu: Optional[torch.Tensor] = None,
|
||||||
):
|
):
|
||||||
bs = len(seq_lens)
|
bs = len(seq_lens)
|
||||||
sm_scale = self.scaling
|
sm_scale = self.scaling
|
||||||
@@ -960,12 +1024,23 @@ class FlashInferMLAIndicesUpdaterPrefill:
|
|||||||
kv_indptr = attn_dcp_metadata.dcp_kv_indptr
|
kv_indptr = attn_dcp_metadata.dcp_kv_indptr
|
||||||
if attn_dcp_metadata.dcp_kv_indices is not None:
|
if attn_dcp_metadata.dcp_kv_indices is not None:
|
||||||
kv_indices = attn_dcp_metadata.dcp_kv_indices
|
kv_indices = attn_dcp_metadata.dcp_kv_indices
|
||||||
kv_len_arr = kv_indptr[1:] - kv_indptr[:-1]
|
# DCP splits kv across CP ranks; the host-side fast-plan arrays are
|
||||||
|
# built from full seq_lens and don't match, so fall back to GPU.
|
||||||
|
qo_indptr_cpu = kv_indptr_cpu = kv_len_arr_cpu = None
|
||||||
|
|
||||||
|
plan_qo_indptr = qo_indptr if qo_indptr_cpu is None else qo_indptr_cpu
|
||||||
|
plan_kv_indptr = kv_indptr if kv_indptr_cpu is None else kv_indptr_cpu
|
||||||
|
plan_kv_len_arr = (
|
||||||
|
kv_indptr[1:] - kv_indptr[:-1]
|
||||||
|
if kv_len_arr_cpu is None
|
||||||
|
else kv_len_arr_cpu
|
||||||
|
)
|
||||||
|
|
||||||
wrapper_paged.plan(
|
wrapper_paged.plan(
|
||||||
qo_indptr,
|
plan_qo_indptr,
|
||||||
kv_indptr,
|
plan_kv_indptr,
|
||||||
kv_indices,
|
kv_indices,
|
||||||
kv_len_arr,
|
plan_kv_len_arr,
|
||||||
self.num_local_heads,
|
self.num_local_heads,
|
||||||
self.kv_lora_rank,
|
self.kv_lora_rank,
|
||||||
self.qk_rope_head_dim,
|
self.qk_rope_head_dim,
|
||||||
@@ -1178,3 +1253,50 @@ def fast_mla_decode_plan(
|
|||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise RuntimeError(f"Error in alternate MLA plan: {e}")
|
raise RuntimeError(f"Error in alternate MLA plan: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def fast_mla_prefill_plan(
|
||||||
|
self,
|
||||||
|
qo_indptr_cpu: torch.Tensor,
|
||||||
|
kv_indptr_cpu: torch.Tensor,
|
||||||
|
kv_indices: torch.Tensor,
|
||||||
|
kv_len_arr_cpu: torch.Tensor,
|
||||||
|
num_heads: int,
|
||||||
|
head_dim_ckv: int,
|
||||||
|
head_dim_kpe: int,
|
||||||
|
page_size: int,
|
||||||
|
causal: bool,
|
||||||
|
sm_scale: float,
|
||||||
|
q_data_type: torch.dtype,
|
||||||
|
kv_data_type: torch.dtype,
|
||||||
|
) -> None:
|
||||||
|
"""Sync-free BatchMLAPagedAttentionWrapper.plan for the target-verify CUDA
|
||||||
|
graph replay. Like fast_mla_decode_plan it hands host-known qo/kv indptr +
|
||||||
|
lengths straight to _cached_module.plan (no per-replay device-to-host copy).
|
||||||
|
Decode's indices updater writes the cuda-graph buffers in place so its fast
|
||||||
|
plan can skip them; verify metadata is freshly built each step, so refresh
|
||||||
|
the bound buffers here exactly as stock plan()'s use_cuda_graph branch does
|
||||||
|
(host->device / device->device, non-blocking).
|
||||||
|
"""
|
||||||
|
self._causal = causal
|
||||||
|
self._page_size = page_size
|
||||||
|
self._sm_scale = sm_scale
|
||||||
|
self._qo_indptr_buf.copy_(qo_indptr_cpu, non_blocking=True)
|
||||||
|
self._kv_indptr_buf.copy_(kv_indptr_cpu, non_blocking=True)
|
||||||
|
self._kv_indices_buf[: len(kv_indices)].copy_(kv_indices, non_blocking=True)
|
||||||
|
self._kv_len_arr_buf.copy_(kv_len_arr_cpu, non_blocking=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._cached_module.plan(
|
||||||
|
self._float_workspace_buffer,
|
||||||
|
self._int_workspace_buffer,
|
||||||
|
self._pin_memory_int_workspace_buffer,
|
||||||
|
qo_indptr_cpu,
|
||||||
|
kv_indptr_cpu,
|
||||||
|
kv_len_arr_cpu,
|
||||||
|
num_heads,
|
||||||
|
head_dim_ckv,
|
||||||
|
causal,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError(f"Error in alternate MLA prefill plan: {e}")
|
||||||
|
|||||||
Reference in New Issue
Block a user