[spec decoding] fully overlap spec decoding for hybrid linear attention backend (#28579)

This commit is contained in:
Qiaolin Yu
2026-06-18 13:31:12 -07:00
committed by GitHub
parent cf0afe3223
commit 2411737244
4 changed files with 25 additions and 6 deletions
@@ -130,6 +130,7 @@ class AscendMambaAttnBackendBase(MambaAttnBackendBase):
forward_mode: ForwardMode, forward_mode: ForwardMode,
spec_info: Optional[SpecInput], spec_info: Optional[SpecInput],
seq_lens_cpu: Optional[torch.Tensor], seq_lens_cpu: Optional[torch.Tensor],
num_padding: Optional[int] = None,
): ):
# out_graph passes seq_lens_cpu=None at capture; mirror the base guard. # out_graph passes seq_lens_cpu=None at capture; mirror the base guard.
if seq_lens_cpu is None: if seq_lens_cpu is None:
@@ -188,6 +188,9 @@ class MambaAttnBackendBase(AttentionBackend):
forward_batch.forward_mode, forward_batch.forward_mode,
forward_batch.spec_info, forward_batch.spec_info,
forward_batch.seq_lens_cpu if not in_capture else None, forward_batch.seq_lens_cpu if not in_capture else None,
num_padding=(
0 if in_capture else getattr(forward_batch, "num_padding", None)
),
) )
def init_forward_metadata(self, forward_batch: ForwardBatch): def init_forward_metadata(self, forward_batch: ForwardBatch):
@@ -441,13 +444,15 @@ class MambaAttnBackendBase(AttentionBackend):
forward_mode: ForwardMode, forward_mode: ForwardMode,
spec_info: Optional[SpecInput], spec_info: Optional[SpecInput],
seq_lens_cpu: Optional[torch.Tensor], seq_lens_cpu: Optional[torch.Tensor],
num_padding: Optional[int] = None,
): ):
if seq_lens_cpu is None: if num_padding is None:
num_padding = 0 if seq_lens_cpu is None:
else: num_padding = 0
num_padding = torch.count_nonzero( else:
seq_lens_cpu == self.get_cuda_graph_seq_len_fill_value() num_padding = torch.count_nonzero(
) seq_lens_cpu == self.get_cuda_graph_seq_len_fill_value()
)
# Make sure forward metadata is correctly handled for padding reqs # Make sure forward metadata is correctly handled for padding reqs
req_pool_indices[bs - num_padding :] = 0 req_pool_indices[bs - num_padding :] = 0
mamba_indices = self.req_to_token_pool.get_mamba_indices(req_pool_indices) mamba_indices = self.req_to_token_pool.get_mamba_indices(req_pool_indices)
@@ -577,6 +582,8 @@ class MambaAttnBackendBase(AttentionBackend):
class Mamba2AttnBackend(MambaAttnBackendBase): class Mamba2AttnBackend(MambaAttnBackendBase):
"""Attention backend wrapper for Mamba2Mixer kernels.""" """Attention backend wrapper for Mamba2Mixer kernels."""
needs_cpu_seq_lens: bool = False
def __init__(self, model_runner: ModelRunner): def __init__(self, model_runner: ModelRunner):
super().__init__(model_runner) super().__init__(model_runner)
config = model_runner.mamba2_config config = model_runner.mamba2_config
@@ -605,6 +612,9 @@ class Mamba2AttnBackend(MambaAttnBackendBase):
forward_batch.forward_mode, forward_batch.forward_mode,
forward_batch.spec_info, forward_batch.spec_info,
forward_batch.seq_lens_cpu if not in_capture else None, forward_batch.seq_lens_cpu if not in_capture else None,
num_padding=(
0 if in_capture else getattr(forward_batch, "num_padding", None)
),
) )
spec_info = forward_batch.spec_info spec_info = forward_batch.spec_info
draft_token_num = spec_info.draft_token_num if spec_info is not None else 1 draft_token_num = spec_info.draft_token_num if spec_info is not None else 1
@@ -699,6 +709,11 @@ class HybridLinearAttnBackend(AttentionBackend):
# Dispatcher aliases the full-attn backend's pool refs. # Dispatcher aliases the full-attn backend's pool refs.
self.token_to_kv_pool = full_attn_backend.token_to_kv_pool self.token_to_kv_pool = full_attn_backend.token_to_kv_pool
self.req_to_token_pool = full_attn_backend.req_to_token_pool self.req_to_token_pool = full_attn_backend.req_to_token_pool
self.max_context_len = getattr(full_attn_backend, "max_context_len", None)
self.needs_cpu_seq_lens = (
full_attn_backend.needs_cpu_seq_lens
or linear_attn_backend.needs_cpu_seq_lens
)
def _is_full_attn( def _is_full_attn(
self, layer: Optional[RadixAttention], layer_id: Optional[int] = None self, layer: Optional[RadixAttention], layer_id: Optional[int] = None
@@ -269,6 +269,8 @@ class GDNKernelDispatcher:
class GDNAttnBackend(MambaAttnBackendBase): class GDNAttnBackend(MambaAttnBackendBase):
"""Attention backend for GDN (Gated Delta Network) linear attention.""" """Attention backend for GDN (Gated Delta Network) linear attention."""
needs_cpu_seq_lens: bool = False
def __init__(self, model_runner: ModelRunner): def __init__(self, model_runner: ModelRunner):
super().__init__(model_runner) super().__init__(model_runner)
self.conv_states_shape = ( self.conv_states_shape = (
@@ -154,6 +154,7 @@ def build_replay_fb_view(
else forward_batch.seq_lens_sum + (bs - raw_bs) * seq_len_fill_value else forward_batch.seq_lens_sum + (bs - raw_bs) * seq_len_fill_value
), ),
seq_lens_cpu=buffers.seq_lens_cpu[:bs], seq_lens_cpu=buffers.seq_lens_cpu[:bs],
num_padding=bs - raw_bs,
encoder_lens=buffers.encoder_lens[:bs] if is_encoder_decoder else None, encoder_lens=buffers.encoder_lens[:bs] if is_encoder_decoder else None,
out_cache_loc=getattr(forward_batch, "out_cache_loc", None), out_cache_loc=getattr(forward_batch, "out_cache_loc", None),
out_cache_loc_dsv4=getattr(forward_batch, "out_cache_loc_dsv4", None), out_cache_loc_dsv4=getattr(forward_batch, "out_cache_loc_dsv4", None),