Repository navigation
refactor: shared DSA indexer forward and top-k schedule, sparse prefill decided once, DCP kernel cleanup - #2776
Merged
Merged
Conversation
Contributor
🏷️ CI GuideRuns automatically on every eligible PR before approval:
Heavy model tests:
|
…ll decided once, DCP kernel cleanup Structure - The model registry (support_model_arch_dict, mono_decode_installers) moves from model_runner.py to atom/models/registry.py. - The DSA indexer op (sparse_attn_indexer) moves from models/deepseek_v2.py to model_ops/indexer/indexer_forward.py. - model_ops/indexer/schedule.py is the one classification of which layers reuse an earlier layer's top-k; the model, the MLA layer and the PP runner all read it. - The MLA metadata builder decides sparse prefill once a step (attn_metadata.run_sparse); MLA, the indexer, PCP and the sglang bridge follow it. - flatten_dsa_config publishes a nested dsa_config as the flat index_* fields; index planes are allocated only for the layers it names. - GDN causal-conv metadata is built from host-side sequence lengths. - atom.model_ops.indexer loads its names on first use, so its kernel-free submodules import without AITER. - Kimi-K3 image request inputs move to atom/models/kimi_k3/image_processing.py, so building them does not import the vision model or AITER. Indexer and sparse MLA - NoPE indexer: index_rope_dim, a fused FP8 query-quant and key-cache writer (model_ops/indexer/nope.py), and keys/head weights projected by the owning attention; the unpooled glm5_next path uses the shared indexer. - A single-rank FP8 prefill subtracts request bases inside the slot conversion instead of rewriting the top-k tensor. - Sparse-prefill work buffers are sized for prefill's own gathered head width; a BF16 sparse-prefill row stays in one work unit, so its result does not depend on batch composition; small BF16 persistent DCP prefills split 128 heads into two groups with one shared buffer set bound by the builder. - BF16 ASM prefill pads 17..127 query heads to the 128 the kernel accepts. - Stable top-k whenever tensor parallelism is on. DCP - The prefill query gather fuses rank-to-token reordering with head padding. - zero_nonfinite_rows checks 32 rows per program; the prefill indptr prefix sum is one launch; the decode candidate exchange uses the custom all-gather; ownership and slot mapping are shared helpers. - The local logits width covers partial interleave groups, and the prefill key gather ignores padded block-table rows. Loading - A layer the checkpoint stores in a plain float dtype is treated as unquantized, decided from the safetensors headers. - Loading warns when a checkpoint float tensor is converted to another float dtype. Kimi-K3 - KDA control parameters, short convolutions and the output norm are FP32; e_score_correction_bias is FP32. - When the config names DSA layers, those layers fuse the indexer key and head-weight projections into the q_a/kv_a projection (K3FusedIndexerProjection). - The BF16 output gate uses the fused sigmoid-mul; BF16 gated norm and attention residual keep one reduction layout at every batch size. - Vision projections run on the aiter BF16 GEMM. Other - MoE: pure tensor parallelism routes pad rows like real rows for now; a -1 route faults the TP a16w4 MoE path. - The multimodal chat endpoint returns prompt_token_ids when requested.
valarLip
force-pushed
the
refactor-dsa-indexer-sparse-mla
branch
from
October 28, 2026 06:43
edf3501 to
29f449e
Compare
The test loads aiter_mla.py with each import replaced; the head-group and top-k schedule modules it now imports had no stand-in, so the real ones were loaded under the stubbed triton and failed.
This was referenced Oct 28, 2026
fused_sigmoid_mul_maybe_quant(quant=False) called AITER's fused_sigmoid_mul directly. Traced by Dynamo, that wrapper's shape checks and mask choice fixed the token count to the one seen while compiling, so a compiled Kimi-K3 broke its graph at the MLA layer and the following layer frame failed with a ConstraintViolationError on hidden_states. The BF16 path is now the guarded op sigmoid_mul, like the FP8 group-quant path: its layout checks run at call time and the graph holds one opaque node. Numerics and the out-of-place contract are unchanged.
charlieguo1106
added a commit
that referenced
this pull request
Oct 29, 2026
Since #2776, pure TP routes pad rows like real rows (mask_pad_rows= layer.use_ep) and passes fused_moe no scheduled_tokens_gpu. The HERD branch of select_experts kept the default mask, so its Top-(K+1) candidates still carried -1 pad rows while fused_moe got no num_local_tokens. aiter's HERD drop leaves -1 rows out only with that count, so pure-TP HERD decode broke. The HERD branch now takes the same mask_pad_rows as _select_logical: pure TP routes its pad rows as real candidates, and a later change to that policy applies to HERD as well.
gbyu-amd
added a commit
that referenced
this pull request
Nov 2, 2026
#2776 gated pad-row masking on EP (`mask_pad_rows=layer.use_ep`) after a suspected "-1 route faults the TP a16w4 MoE". The fault does not reproduce: the TP regression from #2725 was the sort's pad sentinel (real token count) sitting inside the a16w4 gemm2's `token_id < M` bound (padded M), which piled every pad slot into one row. Routing pad rows to -1 is skipped by the sort, so restore it for TP and drop the switch. Kimi-K3 TP8, aiter bd4ff1c70: bs100 decode device time 44.56 -> 42.86 ms/step (-3.8%), c128 TPOT unchanged (102.46 -> 102.41 ms, full batches have no pad rows), GSM8K 0.9666, DSpark GSM8K 0.9651 / acceptance 51.57%, no fault. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
JiaoliangYu
added a commit
that referenced
this pull request
Nov 3, 2026
#2776 gated pad-row masking on EP (`mask_pad_rows=layer.use_ep`) after a suspected "-1 route faults the TP a16w4 MoE". The fault does not reproduce: the TP regression from #2725 was the sort's pad sentinel (real token count) sitting inside the a16w4 gemm2's `token_id < M` bound (padded M), which piled every pad slot into one row. Routing pad rows to -1 is skipped by the sort, so restore it for TP and drop the switch. Kimi-K3 TP8, aiter bd4ff1c70: bs100 decode device time 44.56 -> 42.86 ms/step (-3.8%), c128 TPOT unchanged (102.46 -> 102.41 ms, full batches have no pad rows), GSM8K 0.9666, DSpark GSM8K 0.9651 / acceptance 51.57%, no fault. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: JiaoliangYu <Jiaoliang.Yu@amd.com>
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Structure
support_model_arch_dict,mono_decode_installers) moves frommodel_runner.pytoatom/models/registry.py; docs and skills point there.sparse_attn_indexermoves frommodels/deepseek_v2.pytomodel_ops/indexer/indexer_forward.py.model_ops/indexer/schedule.pyis the one classification of which layers reuse an earlier layer's top-k. The model, the MLA layer (which layer owns a selection and its work plan) and the PP runner (which stage hands its selection on) all read it; the runner's hand-copied version is gone.attn_metadata.run_sparse); MLA, the indexer, PCP and the sglang bridge follow it instead of each re-derivingmax_seqlen_k > index_topk.flatten_dsa_configpublishes a nesteddsa_configas the flatindex_*fields; index planes are allocated only for the layers it names.atom.model_ops.indexerloads its names on first use, so its kernel-free submodules (schedule) import without AITER.atom/models/kimi_k3/image_processing.py, so building them does not import the vision model or AITER.Indexer and sparse MLA
index_rope_dim, a fused FP8 query-quant and key-cache writer (model_ops/indexer/nope.py), and keys/head weights projected by the owning attention. The unpooledglm5_nextpath now uses the shared indexer.attentions/sparse_mla_head_groups.py); the builder owns one buffer set and binds it to the layers that own a selection, and only when no layer shares top-k.DCP
zero_nonfinite_rowschecks 32 rows per program and exits before touching output when all are finite; the prefill indptr prefix sum is one launch; the decode candidate exchange uses the custom all-gather.Loading
Kimi-K3
e_score_correction_biasis FP32.K3FusedIndexerProjection).Other
_select_logical); a -1 route faults the TP a16w4 MoE path.prompt_token_idswhen requested.Test plan
test_row_view_*cases failed only because aiter could not compilemodule_gemm_a8w8_blockscale_bpreshufflein that test environment): index top-k schedule, NoPE writer and FP8 MQA, sparse MLA prefill (batch-composition independence, head groups), DCP topk / merge / prefill query gather / LSE mask, MLA index cache, glm5_next kpool, FP4 indexer, DeepSeek-V4.1 indexer and paged scoring, Kimi-K3 activations and norm batch invariancetests/minustests/pluginandtest_openai_server.py); the only failures should be ones that already fail on main