Keep CP compute padding out of sparse MoE

CP shared-KV compute padding creates per-request lane slots, so valid rows are not a simple prefix/suffix mask. DeepEP MoE was still seeing dummy rows and using scalar non-padded semantics, which let padding participate in gate/topk and corrupted cache-hit tiny-extend inference.\n\nThe fix compacts CP-local valid rows before MoE dispatch and restores the compact output back to the compute-padded row layout before downstream layer communication. The local GSM8K investigation ledger is now removed from the tracked tree and ignored so future debug notes stay local.\n\nConstraint: CP shared-KV compute-padding layout must keep downstream communicator shapes stable.\nRejected: Disable bs>1/current reuse/cache-hit fast paths | hides the semantic bug and loses the intended performance path.\nRejected: Use num_token_non_padded for MoE under compute padding | valid rows are interleaved with dummy lane slots, not suffix-padded.\nConfidence: high\nScope-risk: moderate\nDirective: Do not feed compute-padded dummy rows into sparse MoE gate/topk; compact valid rows at the MoE boundary and restore shape afterward.\nTested: python -m py_compile python/sglang/srt/layers/attention/nsa/utils.py python/sglang/srt/models/deepseek_v2.py\nTested: remote focused CP utils tests passed, 4 tests.\nTested: remote GSM8K 50-question smoke accuracy 0.960; 200-question runs accuracy 0.955 and 0.965; full 1319-question run accuracy 0.952.\nNot-tested: Long-running production traffic beyond GSM8K after this commit.
This commit is contained in:
laoyao0822
2026-06-09 01:48:24 +08:00
parent 4d2fdd14ac
commit 50fde834ae
5 changed files with 130 additions and 2309 deletions
@@ -1905,6 +1905,56 @@ class TestNSAInSeqCPUtils(unittest.TestCase):
self.assertEqual(selected.tolist(), [[50.0, 51.0]])
def test_restore_cp_local_valid_rows_for_moe_keeps_dummy_rows_zero(self):
import torch
from sglang.srt.layers.attention.nsa.utils import (
restore_cp_local_valid_rows_for_moe,
)
plan = build_batch_page_aligned_in_seq_split_plan(
extend_lens=[29, 9, 60, 9, 17],
prefix_lens=[704, 704, 640, 704, 704],
page_size=64,
cp_size=8,
cp_rank=0,
)
forward_batch = SimpleNamespace(
nsa_cp_metadata=NSAContextParallelMetadata(
batch_size=5,
batch_plan=plan,
)
)
local_compute_rows = torch.zeros(
(sum(plan.request_compute_rank_local_tokens), 2), dtype=torch.float32
)
compact_valid = torch.arange(
sum(plan.request_valid_rank_local_tokens) * 2, dtype=torch.float32
).view(-1, 2)
restored = restore_cp_local_valid_rows_for_moe(
forward_batch,
compact_valid,
local_compute_rows,
)
self.assertEqual(tuple(restored.shape), tuple(local_compute_rows.shape))
valid_rows = select_cp_local_valid_rows_for_cache_write(
forward_batch,
restored,
)
self.assertTrue(torch.equal(valid_rows, compact_valid))
valid_mask = torch.zeros(restored.shape[0], dtype=torch.bool)
cursor = 0
for compute_len, valid_len in zip(
plan.request_compute_rank_local_tokens,
plan.request_valid_rank_local_tokens,
):
valid_mask[cursor : cursor + valid_len] = True
cursor += compute_len
self.assertTrue(torch.equal(restored[~valid_mask], torch.zeros_like(restored[~valid_mask])))
def test_select_cp_current_valid_rows_accepts_global_rows_under_compute_padding(
self,
):