From 92b800c531b33009ba7b412087740284eba8a943 Mon Sep 17 00:00:00 2001 From: Kaixi Date: Sat, 4 Jul 2026 11:50:26 +0200 Subject: [PATCH] [DSA][GLM5.2] Index Share for MHA (#29959) --- .../attention_forward_methods/forward_mha.py | 17 +++++---- .../attention_forward_methods/forward_mla.py | 38 +++++++++++-------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mha.py b/python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mha.py index 9b2963627..cd8f89332 100644 --- a/python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mha.py +++ b/python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mha.py @@ -169,14 +169,15 @@ class DeepseekMHAForwardMixin: q = self.q_b_proj(q_lora)[0].view( -1, self.num_local_heads, self.qk_head_dim ) - _ = self.indexer( - x=hidden_states, - q_lora=q_lora, - positions=positions, - forward_batch=forward_batch, - layer_id=self.layer_id, - return_indices=False, - ) + if self.should_run_indexer(): + _ = self.indexer( + x=hidden_states, + q_lora=q_lora, + positions=positions, + forward_batch=forward_batch, + layer_id=self.layer_id, + return_indices=False, + ) elif _use_aiter_gfx95 and self.q_b_proj.weight.dtype == torch.uint8: # MXFP4: fused RMSNorm + quant q, _, _, _ = fused_rms_mxfp4_quant( diff --git a/python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mla.py b/python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mla.py index 200951e67..f48a55f70 100644 --- a/python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mla.py +++ b/python/sglang/srt/models/deepseek_common/attention_forward_methods/forward_mla.py @@ -178,6 +178,27 @@ class DeepseekMLAForwardMixin: get_global_server_args().flashinfer_mla_disable_ragged ) + def should_run_indexer( + self: DeepseekV2AttentionMLA, + prev_topk_indices: Optional[torch.Tensor] = None, + ) -> bool: + """Whether this layer runs its own indexer vs reusing carried topk. + + skip_topk (shared) layers carry no indexer weights in the checkpoint, + so they must reuse the carried topk and never run the indexer. Do NOT + widen this to `or prev_topk_indices is None` (the upstream gate): that + recomputes with an uninitialized indexer whenever cross-layer + propagation is unavailable (e.g. the TBO op path drops topk_indices), + reintroducing the >index_topk garbling. The is_nextn clause is the + sole intentional fallback (the NextN layer has its own weights). + + Eager-MHA prefill calls this with no argument: it needs no topk for + the current forward, but producer layers must still fill their indexer + K cache for later MLA/decode; shared layers' cache is never read, so + filling it is dead work. + """ + return not self.skip_topk or (self.is_nextn and prev_topk_indices is None) + def _can_fuse_bmm_into_attention( self: DeepseekV2AttentionMLA, forward_batch: ForwardBatch ) -> bool: @@ -353,15 +374,7 @@ class DeepseekMLAForwardMixin: q = self.q_b_proj(q)[0].view( -1, self.num_local_heads, self.qk_head_dim ) - # skip_topk (shared) layers carry no indexer weights in the - # checkpoint, so they must reuse the carried topk and never run - # the indexer. Do NOT widen this to `or prev_topk_indices is - # None` (the upstream gate): that recomputes with an - # uninitialized indexer whenever cross-layer propagation is - # unavailable (e.g. the TBO op path drops topk_indices), - # reintroducing the >index_topk garbling. The is_nextn clause is - # the sole intentional fallback (layer 78 has its own weights). - if not self.skip_topk or (self.is_nextn and prev_topk_indices is None): + if self.should_run_indexer(prev_topk_indices): topk_indices = self.indexer( x=hidden_states, q_lora=q_lora, @@ -387,12 +400,7 @@ class DeepseekMLAForwardMixin: fusion_plan = self._make_mla_bmm_fusion_plan(q, q_nope) if q_lora is not None: - # See the skip_topk note above: shared layers have no - # indexer weights, so this gate must not fall back to - # computing when prev_topk_indices is None. - if not self.skip_topk or ( - self.is_nextn and prev_topk_indices is None - ): + if self.should_run_indexer(prev_topk_indices): topk_indices = self.indexer( x=hidden_states, q_lora=q_lora,