Skip to content

[None][feat] Port DFlash weight-load checks, DSpark norm_dim and disagg tests from the Rubin branch - #19185

Draft
reasonsolo wants to merge 3 commits into
NVIDIA:mainfrom
reasonsolo:user/lizhiz/mb-runtime
Draft

reasonsolo wants to merge 3 commits into
NVIDIA:mainfrom
reasonsolo:user/lizhiz/mb-runtime

Conversation

@reasonsolo

Copy link
Copy Markdown
Collaborator

Description

Ports the portable subset of the Rubin branch's runtime work — DFlash weight-load checks, the DSpark norm_dim fused path, and disaggregated-serving test coverage.

  • models/modeling_dflash.py_assert_backbone_complete weight-load validation, folded around main's FA4 backend so the _uses_worker_attention_backend gate covers all three backends rather than two. modeling_utils.py hoists FUSED_MODULE_COMPONENTS from an inline dict literal to a module constant so the check can use it (no behaviour change).
  • speculative/dflash.pyctx_len_now clamp fix.
  • cute_dsl_kernels/blackwell/dspark_rmsnorm_rope.py + its custom op — norm_dim support. Also fixes a Rubin omission: cute_dsl_dspark_rmsnorm_rope did not forward norm_dim to its own support predicate, so the fused path silently never engaged for the non-default case.
  • Disagg tests: test_disaggregated_multinode.py (new), test_disagg_index_mapper_early_release.py, part of test_cache_reuse_adapter.py.

Scope: 11 of 31 candidate files

Two thirds of the candidate set was dropped after verification, because the production half is missing from both main and the Rubin branch — these are test-only cherry-picks that are already red at their source:

Dropped Missing from both trees
test_kv_cache_localization.py (+848) the entire locality-domain KV-cache API
test_dspark_attention.py (+905) _output_projection, _qkv_projections — module-level import, so the whole file fails to collect, including tests main currently passes
test_kimi_k3_dspark_semantics.py (+718) MLADSparkForCausalLM + yarn-rope helpers (~700 of 718 lines)
test_benchmark_disagg.py four _benchmark_* attrs on PyExecutor
speculative/interface.py, eagle3.py attn_metadata.ugpu_enabled, draft_kv_cache_manager.fork_join_attn
speculative/model_drafter.py file deleted from main by #18721

Also excluded: scheduler_v2.py, which contains a duplicate _try_recompute_pause_for_gen definition (lines 1477 and 1560) where the second shadows the first and bypasses main's newer _try_allocate_generation, losing the joint draft-pool check; and its equal-cost context chunking is env-gated off and incomplete (commit_ctx_cost is never called, so the budget never binds).

The K3DsparkForCausalLM architecture registration was dropped: _arch_index.py maps it to modeling_dspark, but no such class exists there on main. It is also an unreleased Inferact/Kimi-K3-DSpark name, so it would need architecture_allowlist.py clearance it cannot currently get.

Test Coverage

No new test-list entries needed — both ported unit tests land in directories already covered by directory-level entries. test_disaggregated_multinode.py is not registered: it requires a 2-node / 1-task-per-node Slurm allocation that no current stage provides, so registering it into an existing GB300 2-node stage would only ever skip. The exact srun invocation is in the file header; this needs a CI owner's decision.

⚠️ Not verified

No test has been executed and nothing has been built. Static verification only: hunk-by-hunk review, python3 -m compileall, and pre-commit.

The norm_dim production change lands untested on main — its test is blocked on _rmsnorm_rope_batched(norm_dim=) in modeling_dspark.py, which is outside this PR. It is additive and bit-identical at the default; if carrying an untested knob is unwelcome, those two files can be dropped.

PR Checklist

  • PR description explains what and why
  • Follows TRT-LLM coding guidelines to the best of our knowledge
  • Test cases provided but NOT executed — see above
  • No API changes
  • Reviewers appropriate for the PR

🤖 Generated with Claude Code

@dc3671

dc3671 commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

DFlash/DSpark related changes will be merged in this PR: #19040

@reasonsolo reasonsolo changed the title [None][chore] Port DFlash weight-load checks, DSpark norm_dim and disagg tests from the Rubin branch [None][feat] Port DFlash weight-load checks, DSpark norm_dim and disagg tests from the Rubin branch Sep 16, 2026
…agg tests from the Rubin branch

Merge-back of the portable subset of the Rubin runtime work. Most of the
originally scoped feature (KV cache manager v2 uGPU localization, the DSpark
MLA drafter, the disagg benchmark fill gate) is NOT included: its production
half is absent from the source branch as well as from main, so porting the
corresponding tests would have added red tests rather than coverage. See the
PR description for the per-file disposition.

Production:

* modeling_dflash: fail loudly when a draft checkpoint omits backbone weights
  the drafter does not share with the target. `_assert_backbone_complete`
  walks the constructed module tree rather than a hand-kept list, and
  `WRAPPER_OWNED_WEIGHTS` gates the wrapper-owned `fc` / `hidden_norm` that a
  module walk cannot see. Previously `allow_partial_loading=True`, needed for
  the target-shared `embed_tokens` / `lm_head`, let a truncated checkpoint
  load clean and leave parameters at `torch.empty`. Also restructures the
  attention-backend init so every op handle starts at None and adds the
  `_uses_worker_attention_backend` hook for drafters that bring their own
  block decode; the FA4 backend added on main is preserved.

* modeling_utils: hoist the fused-module component table out of
  `_load_weights_impl` into `FUSED_MODULE_COMPONENTS` so the check above reads
  the same table the loader uses instead of keeping a third copy. No
  behavior change.

* modeling_speculative: bound an external drafter's position tables with the
  target's `max_seq_len`. Without it the drafter falls back to the
  checkpoint's advertised `max_position_embeddings` and allocates a table
  sized for a context the runtime never serves.

* speculative/dflash: clamp the running context length before deriving the
  block-decode query positions. `_ctx_len` is clamped to `_max_ctx` only after
  the step's accepted tokens are folded in, so a request already at the
  ceiling indexed `num_accepted` positions past the end of the sequence.

* dspark_rmsnorm_rope: add an optional `norm_dim` so the fused RMSNorm/RoPE
  kernel can normalize a prefix of the row and pass the remaining rope lanes
  through raw, as DeepSeek-style MLA needs. Defaults to the whole row and is
  bit-identical to the previous behavior there. `norm_dim` is now also
  forwarded to the support predicate, which the source branch omitted.

* kv_cache_manager_v2/_block_radix_tree: document why the stale-tail prune
  requires every life cycle to be pageless, mirroring the C++ implementation.

Tests:

* New `test_block_radix_tree_stale_prune` covers that predicate, including the
  negative control that dead tails still get pruned.

* New `test_disaggregated_multinode` is a two-node DSpark disagg accuracy
  harness. It skips unless run under a 2-node, 1-task-per-node Slurm
  allocation; the exact srun invocation is in the file header. It is
  deliberately left out of the test lists, as no current stage uses that
  layout.

* `test_disagg_index_mapper_early_release` gains a case asserting that
  `release_index_slot` detaches every page-index view before the slot is
  reused. The import is adjusted for the `pyexecutor.kv_cache` package move.

* `test_cache_reuse_adapter` gains a case for the DSpark gen-init draft
  reserve being removed before the SWA trim.

Signed-off-by: Lizhi Zhou <1432185+reasonsolo@users.noreply.github.com>
…dtype

An external drafter has its own KV cache, sized and allocated separately from
the target's. It was inheriting the target's quantization config, so an fp8-KV
target silently forced the drafter's pool to fp8 as well -- charging the budget
at a dtype the drafter never asked for and does not necessarily support.

Neutralize the target's KV quant config when building the draft cache manager
for an external drafter, and fall the draft KV dtype back to "auto" when the
inherited value is "fp8".

Ported from the Rubin branch, where this landed as 33624323a6 and was then lost
when a later replay commit (2ba428f26f) re-took pyexecutor/_util.py from main.
Restored on the internal branch as 8626bd6577; content survival of the original
measured 0%.

NOT RUN: no GPU here. Needs an external-drafter plus fp8-KV-target speculative
decoding run to validate.

Signed-off-by: Lizhi Zhou <1432185+reasonsolo@users.noreply.github.com>
…the rebase

On user/lizhiz/rubin-advance, configure_kv_cache_capacity re-derives
max_tokens from the final estimated budget once estimation finishes whenever
the V2 KV cache manager is in use. The rebase onto main dropped that block;
main never had it either.

Without it, a one-model speculative-decoding draft KV cache that shares the
config reads the same max_gpu_total_bytes as the target. That cap does not
scale with the draft's much smaller per-token footprint, which depends on
num_local_layers, so both managers claim the whole budget and the draft
OOMs. Deriving max_tokens restores V1's behaviour: V2's quota becomes
min(max_gpu_total_bytes, max_tokens * bytes_per_token), which picks the
layer-scaled draft budget when the draft manager reads the shared config. An
explicit user-provided max_tokens still wins.

Restored as it stands on rubin-advance, using this branch's existing
self._is_kv_cache_manager_v2 attribute rather than recomputing the issubclass
check locally.

Signed-off-by: Lizhi Zhou <1432185+reasonsolo@users.noreply.github.com>
@reasonsolo
reasonsolo force-pushed the user/lizhiz/mb-runtime branch from 0de2ee5 to b493415 Compare September 18, 2026 02:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants