Skip to content

[None][fix] enable static EPLB for the one-model DSpark drafter - #16938

Merged
longlee0622 merged 1 commit into
NVIDIA:mainfrom
longlee0622:dev/dspark-static-eplb-pr
Jul 29, 2026
Merged

[None][fix] enable static EPLB for the one-model DSpark drafter#16938
longlee0622 merged 1 commit into
NVIDIA:mainfrom
longlee0622:dev/dspark-static-eplb-pr

Conversation

@longlee0622

@longlee0622 longlee0622 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Enable static MoE EPLB for the one-model DSpark drafter. With EPLB configured, the
DSpark gen worker currently dies at model init:

File ".../models/modeling_dspark.py", line 322, in __init__      <- DSparkDraftModel
File ".../models/modeling_deepseekv4.py", line 1817, in __init__  <- DeepseekV4MoE
File ".../modules/fused_moe/interface.py", line 549, in _init_load_balancer
    assert moe_load_balancer_config is not None
AssertionError

The target model is built inside maybe_create_moe_load_balancer(...), so the
engine-wide MoeLoadBalancer is live while the DSpark stages are constructed. But
the generic external-drafter branch builds draft_config from the speculative
checkpoint without propagating moe_load_balancer, so every DSpark stage MoE sees

get_moe_load_balancer()               -> active manager
draft_config.moe_load_balancer        -> None

DSparkDraftModel._derive_draft_model_config() is not the loss point — its shallow
copy preserves whatever it receives; the config is already None by then.

Changes

1. Propagate moe_load_balancer — DSpark only.
external_drafter_config_kwargs() factors out the kwargs the one-model external
drafter inherits, and adds moe_load_balancer only when
spec_dec_mode.is_dspark(). DSpark's stages are full DeepSeek-V4 blocks sharing the
target's expert topology and layer-index namespace (layer_idx = num_hidden_layers + stage_id), so they can register into the target's balancer. PARD / DFlash /
draft-target are independent checkpoints whose topology and layer numbering need not
match the target's, and whose EPLB configs would be keyed against a different
namespace — their kwargs are byte-for-byte unchanged, and a test pins that so a
future refactor cannot silently generalize this.

2. Validate the EPLB config covers the DSpark stages.
A config generated before DSpark (or in the 1-layer-MTP era) lacks the stage
indices, which today surfaces as a bare KeyError: layer_idx 62 not found from deep
inside MoE init. The new check runs before any DSpark MoE is built and reports every
missing index at once, with the layer-index convention and a pointer to the
regeneration docs.

3. Reject online EPLB (layer_updates_per_iter > 0) at config time.
DSpark supports static EPLB only. Online EPLB requires every registered MoE layer to
run exactly once per iteration, but the DSpark draft MoE is skipped on iterations
with no generation requests anywhere (context-only batches, warmup). The balancer's
CPU worker then spins forever in its untimed waitCpuStage(), waiting for a GPU
signal only an MoE forward emits — a silent deadlock. Failing at startup beats
hanging in production.

Also: when EPLB is active, require the draft config's num_hidden_layers to match
the target's, since the stage indices derive from it and must line up with the
namespace initial_global_assignments is keyed by.

All three validations are gated on get_moe_load_balancer() being non-None — the
exact condition under which MoE._init_load_balancer consumes the config — so the
non-EPLB path is untouched.

Test Coverage

Unittests/unittest/_torch/speculative/hw_agnostic/test_dspark_eplb_config.py,
14 CPU-only cases: propagation, PARD/DFlash/draft-target isolation, spec_config=None
preservation, stage-layer coverage, auto-placement, EPLB-inactive no-op, online-EPLB
rejection, layer-base match/mismatch.

Multi-GPU smoke (no CI stage covers DSpark + EPLB + real weights, so this was run
by hand):

Case Setup Result
Missing stage layers DeepSeek-V4-Pro-DSpark, 8x B200, TP8/EP8, a real deployment EPLB config covering layers 0..61 ValueError: initial_global_assignments is missing DSpark layer(s) [62, 63]. ... indices [61, 64). on all 8 ranks — the old AssertionError is gone
Full coverage DeepSeek-V4-Flash-DSpark, 4x B200, TP4/EP4, config covering 0..45 Model constructs; EPLB registers layers 0..45 = num_hidden_layers(43) + num_stages(3), i.e. the DSpark stages land on 43/44/45 as designed; weight load, register_weight_slots_after_to_cuda() and finalize_model() all pass

The full-coverage run stops in _run_attention_warmup on a CuTe-DSL codegen error
(nvgpu.cvt_fptrunc operand type), so it does not reach token generation. That
failure is unrelated to this change: an otherwise identical run with EPLB fully
disabled fails identically.

Notes for reviewers

  • Scope is deliberately DSpark-only; generalizing to other external drafters needs a
    per-drafter EPLB config domain and layer identity design first.
  • No user-facing or nested config fields are added, so the LLM args golden manifest
    does not change.
  • Pre-existing issue, out of scope: any construction-time exception raised after
    MoeLoadBalancer(...) exists leaves MpiPoolSession.shutdown blocked joining its
    workers forever, while they still hold GPU contexts. Confirmed with a control that
    fails from pre-existing code (interface.py assignment-length check) on target
    layer 0, with these validators inactive — it hangs identically. Worth a separate
    issue: it turns a fast, clear config error into a wedged node.

PR Checklist

  • PR title follows [JIRA/NVBUG/None][type] description
  • New unit tests added and passing locally
  • Pre-commit hooks pass
  • DCO signed off

Dev Engineer Review

  • Adds DSpark-only static EPLB propagation and validation.
  • Enforces complete stage-layer assignments and matching draft/target layer counts.
  • Preserves behavior when EPLB is inactive.
  • No configuration or test-list files were changed.
  • CI merge pipelines failed twice and require investigation before retriggering.

QA Engineer Review

Added 14 CPU-only unit tests covering propagation, drafter isolation, layer coverage, inactive/online EPLB behavior, and layer-count validation.

No corresponding entries were added to tests/integration/test_lists/test-db/ or qa/. Existing DSpark performance entries in qa/llm_perf_core.yml do not cover this unit-test file.

Verdict: needs follow-up

The one-model external-drafter branch builds the draft ModelConfig from the
speculative checkpoint but never propagated moe_load_balancer. With EPLB
enabled, the engine-wide MoeLoadBalancer is live during DSpark construction
while the draft config's moe_load_balancer is None, so every DSpark stage MoE
trips `assert moe_load_balancer_config is not None` in
MoE._init_load_balancer and the gen worker dies at model init.

Propagate moe_load_balancer for DSpark only. DSpark's draft stages are full
DeepSeek-V4 blocks sharing the target's expert topology and layer-index
namespace (layer_idx = num_hidden_layers + stage_id), so they register into
the target's balancer as additional EPLB layers. PARD, DFlash and
draft-target are independent checkpoints whose topology and layer numbering
need not match the target's; their kwargs are unchanged and a test pins that.

Add two config-time validations, both gated on an actually-active balancer so
the non-EPLB path is untouched:

  - initial_global_assignments must cover the DSpark stage indices. A config
    generated before DSpark (or with 1-layer MTP) lacks them, which otherwise
    surfaces as a bare KeyError from deep inside MoE init; now it reports every
    missing index at once and points at the regeneration docs.
  - reject layer_updates_per_iter > 0. DSpark supports static EPLB only:
    online EPLB requires every registered MoE layer to run exactly once per
    iteration, but the draft MoE is skipped on iterations with no generation
    requests (context-only batches, warmup), and the balancer's CPU worker
    then spins forever in its untimed waitCpuStage(). Failing at startup beats
    a silent deadlock.

Also require the draft config's num_hidden_layers to match the target's when
EPLB is active, since the stage layer indices are derived from it and must
line up with the namespace initial_global_assignments is keyed by.

Signed-off-by: Jonas Li <6110159+longlee0622@users.noreply.github.com>
@longlee0622
longlee0622 force-pushed the dev/dspark-static-eplb-pr branch from 0b5730b to f1a8aa8 Compare July 28, 2026 08:11
@longlee0622

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62157 [ run ] triggered by Bot. Commit: f1a8aa8 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62157 [ run ] completed with state SUCCESS. Commit: f1a8aa8
/LLM/main/L0_MergeRequest_PR pipeline #50332 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@longlee0622

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62170 [ run ] triggered by Bot. Commit: f1a8aa8 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62170 [ run ] completed with state SUCCESS. Commit: f1a8aa8
/LLM/main/L0_MergeRequest_PR pipeline #50343 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@longlee0622

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@longlee0622
longlee0622 marked this pull request as ready for review July 28, 2026 21:16
@longlee0622
longlee0622 requested review from a team as code owners July 28, 2026 21:16
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

DSpark EPLB integration

Layer / File(s) Summary
DSpark EPLB validation
tensorrt_llm/_torch/models/modeling_dspark.py, tests/unittest/_torch/speculative/hw_agnostic/test_dspark_eplb_config.py
Adds layer-base, static-only, and stage-assignment validation before DSpark stage construction, with tests for valid, invalid, inactive, and auto-placement cases.
External drafter configuration wiring
tensorrt_llm/_torch/models/modeling_speculative.py, tests/unittest/_torch/speculative/hw_agnostic/test_dspark_eplb_config.py
Centralizes external drafter kwargs, disables recursive speculative decoding, propagates the DSpark load balancer, and validates draft/target layer alignment.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SpecDecOneEngineForCausalLM
  participant external_drafter_config_kwargs
  participant ModelConfig
  SpecDecOneEngineForCausalLM->>external_drafter_config_kwargs: Build external drafter kwargs
  external_drafter_config_kwargs->>ModelConfig: Pass copied settings with spec_config=None
  external_drafter_config_kwargs-->>ModelConfig: Include moe_load_balancer for DSpark
Loading

Suggested reviewers: schetlur-nv

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, follows the required template, and accurately describes the main DSpark EPLB change.
Description check ✅ Passed The description covers Summary, Changes, Test Coverage, and PR Checklist with enough detail for review.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tensorrt_llm/_torch/models/modeling_dspark.py`:
- Around line 82-120: Annotate every affected function without using Any: in
tensorrt_llm/_torch/models/modeling_dspark.py lines 82-120, add concrete
parameter and return types to _active_moe_load_balancer,
validate_dspark_eplb_layer_base, and validate_dspark_eplb_stage_layers, plus
Google-style argument documentation for the public validators; in
tests/unittest/_torch/speculative/hw_agnostic/test_dspark_eplb_config.py lines
46-76, type the helpers and fixture generator; in the same file lines 87-210,
annotate test parameters and None returns; and in
tensorrt_llm/_torch/models/modeling_speculative.py lines 1842-1869, replace the
bare dict return annotation with precise input and mapping types.
- Around line 156-159: Update the assignment guard in the DSpark
placement-validation logic to bypass validation only when
initial_global_assignments is None. Preserve validation for an explicitly
supplied empty mapping so missing layer assignments are rejected before EPLB
initialization.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 55c3d84a-deb4-454e-8b54-e4cce547704d

📥 Commits

Reviewing files that changed from the base of the PR and between 2a7231d and f1a8aa8.

📒 Files selected for processing (3)
  • tensorrt_llm/_torch/models/modeling_dspark.py
  • tensorrt_llm/_torch/models/modeling_speculative.py
  • tests/unittest/_torch/speculative/hw_agnostic/test_dspark_eplb_config.py

Comment thread tensorrt_llm/_torch/models/modeling_dspark.py
Comment thread tensorrt_llm/_torch/models/modeling_dspark.py
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62308 [ run ] triggered by Bot. Commit: f1a8aa8 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62308 [ run ] completed with state FAILURE. Commit: f1a8aa8
/LLM/main/L0_MergeRequest_PR pipeline #50476 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@longlee0622

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62385 [ run ] triggered by Bot. Commit: f1a8aa8 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #62385 [ run ] completed with state SUCCESS. Commit: f1a8aa8
/LLM/main/L0_MergeRequest_PR pipeline #50549 completed with status: 'SUCCESS'

CI Report

Link to invocation

@longlee0622
longlee0622 merged commit 2b2bf97 into NVIDIA:main Jul 29, 2026
12 checks passed
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.

4 participants