Skip to content

[Fix] dsv4: Build the DSpark draft block metadata without host sequence lengths - #46891

Merged
hnyls2002 merged 1 commit into
sgl-project:mainfrom
kpham-sgl:dsv4-dspark-draft-no-host-lens
Oct 27, 2026
Merged

hnyls2002 merged 1 commit into
sgl-project:mainfrom
kpham-sgl:dsv4-dspark-draft-no-host-lens

Conversation

@kpham-sgl

@kpham-sgl kpham-sgl commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Motivation

Since #45925, the DSpark draft batch carries no host sequence lengths when the draft attention backend sets needs_cpu_seq_lens = False. That is right for trtllm_mha, but the DeepSeek-V4 backend also sets it to False under DSpark on CUDA. Its draft-block prep, init_forward_metadata_dspark_draft_block, then read the lengths back with seq_lens.tolist(), which caused two problems:

  • The metadata glue graph turned off. The first glue capture of each server fails with Cannot copy between CPU and CUDA tensors during CUDA graph capture, and the runner falls back to eager draft metadata prep for the rest of the run:
    Metadata glue-graph capture failed for key (1, '5', '5'); falling back to eager metadata prep permanently.
    
  • A device-to-host sync on every draft step. From then on, every eager prep calls seq_lens.tolist() again.

On CUDA the draft-block prep does not need host lengths at all:

  • The extend lengths are [block_size] * bs.
  • The causal expand runs its Triton kernel from the device tensors and drops the host lists.
  • Compression is off for the draft block.

Modifications

  • compute_uniform_extend_lengths accepts seq_lens_cpu=None. It then takes the batch size from seq_lens and returns no host copy of the extended lengths.
  • The DeepSeek-V4 draft-block prep passes None through instead of calling seq_lens.tolist().
  • One CPU test for the helper, with and without host lengths.

Accuracy Tests

The draft metadata comes from the same device tensors as before. Accept length is unchanged with real drafts: DEP2 with real acceptance gives a median of 2.0 to 2.2 on both arms at every batch size.

Speed Tests and Profiling

GB300, DeepSeek-V4.1-Flash, DSpark block 5, one node per arm, one run each. Main is cb55d77d37, and the fix is the same commit plus this change. sa-bench uses input length 1024. The output length is 4096 for TP4 and 1024 for DEP2. Forward occupancy comes from SGLANG_ENABLE_METRICS_DEVICE_TIMER=1.

TP4, EP1, simulated acceptance 3.51:

Concurrency Arm Glue capture failures Output tok/s Median ITL (ms) Median TPOT (ms) Forward occupancy
1 main 4 562.0 5.91 1.69 94.3%
1 fix 0 655.5 5.01 1.44 98.7%
8 main 4 2,081.6 10.08 3.25 97.6%
8 fix 0 2,122.2 9.29 3.04 99.2%

DEP2 with DP attention and MegaMoE, real acceptance:

Concurrency Arm Glue capture failures Output tok/s Median ITL (ms) Median TPOT (ms)
8 main 2 946.7 13.68 7.10
8 fix 0 915.9 12.90 7.26
64 main 2 3,829.6 25.64 16.17
64 fix 0 3,773.8 25.12 16.19

The gain shows where the step is short and host-bound: at TP4 batch 1, output throughput is 17% higher and the median ITL is 15% lower. DEP2 is within run-to-run noise.

Checklist

🤖 Generated with Claude Code


CI States

Latest PR Test (Base): ❌ Run #40592800125
Latest PR Test (Extra): ❌ Run #40592800066
Latest PR Test (AMD ROCm 10): ❌ Run #40592799993

…ce lengths

Since sgl-project#45925 the DSpark draft batch carries no host sequence lengths when the
draft backend opts out of them, which the DeepSeek-V4 backend does on CUDA. Its
draft-block prep then read them back with seq_lens.tolist(): a device sync on
every draft step, and inside the metadata glue capture an error that turned the
glue graph off. The prep needs only the batch size and the device tensors, so
derive the extend lengths from those.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kpham-sgl

kpham-sgl commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator Author

/rerun-test test_ragged_verify.py test_dsv41_draft_metadata_glue.py test_dsv41_target_verify_metadata_glue.py test_forked_draft_metadata_prep.py test_dspark_draft_block.py test_fused_draft_placement.py test_dspark_block_accept_estimator.py test_dspark_confidence_metrics.py test_dspark_dp_tier.py test_dspark_draft_path_default.py test_dspark_info_dumper.py test_dspark_scheduler.py test_dspark_sps_table.py test_dspark_sts.py test_dspark_draft_graph.py test_dspark_kernel_parity.py test_dspark_stacked_ctx_kv_parity.py test_ragged_verify_backend_capability.py test_dspark_step_inputs_once.py test_dspark_sampling_accept_graph.py test_basic_sanity_dspark.py registered/spec/dspark/test_dspark_dp_spec_prefill_coordination.py test_dspark_minimax_mxfp8_stacked_kv.py test_deepseek_v41_flash_gb300_dp2ep2.py test_deepseek_v41_flash_gb300_tp2ep2.py test_deepseek_v41_flash_gb300_encoder_bcg.py test_deepseek_v41_flash_gb300_trtllm_bounded_bcg.py test_deepseek_v41_pd_cp_dspark.py test_deepseek_v4_flash_fp4_b200.py test_deepseek_v4_flash_fp4_megamoe_b200.py
(2 tries)

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

🚀 ubuntu-latest (2 tests): ✅ View workflow run

cd test/ && python3 registered/spec/dspark/test_ragged_verify.py
cd test/ && python3 registered/unit/model_executor/runner/test_dsv41_draft_metadata_glue.py

🚀 1-gpu-5090 (1 test): ✅ View workflow run

cd test/ && python3 registered/spec/dspark/test_dspark_draft_graph.py

🚀 2-gpu-gb300 (1 test): ✅ View workflow run

cd test/ && python3 registered/e2e/models/test_deepseek_v41_flash_gb300_dp2ep2.py

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

🚀 ubuntu-latest (14 tests): ✅ View workflow run

cd test/ && python3 registered/spec/dspark/test_ragged_verify.py
cd test/ && python3 registered/unit/model_executor/runner/test_dsv41_draft_metadata_glue.py
cd test/ && python3 registered/unit/model_executor/runner/test_dsv41_target_verify_metadata_glue.py
cd test/ && python3 registered/unit/model_executor/runner/test_forked_draft_metadata_prep.py
cd test/ && python3 registered/unit/spec/test_dspark_draft_block.py
cd test/ && python3 registered/unit/mem_cache/test_fused_draft_placement.py
cd test/ && python3 registered/spec/dspark/test_dspark_block_accept_estimator.py
cd test/ && python3 registered/spec/dspark/test_dspark_confidence_metrics.py
cd test/ && python3 registered/spec/dspark/test_dspark_dp_tier.py
cd test/ && python3 registered/spec/dspark/test_dspark_draft_path_default.py
cd test/ && python3 registered/spec/dspark/test_dspark_info_dumper.py
cd test/ && python3 registered/spec/dspark/test_dspark_scheduler.py
cd test/ && python3 registered/spec/dspark/test_dspark_sps_table.py
cd test/ && python3 registered/spec/dspark/test_dspark_sts.py

🚀 1-gpu-5090 (6 tests): ✅ View workflow run

cd test/ && python3 registered/spec/dspark/test_dspark_draft_graph.py
cd test/ && python3 registered/spec/dspark/test_dspark_kernel_parity.py
cd test/ && python3 registered/spec/dspark/test_dspark_stacked_ctx_kv_parity.py
cd test/ && python3 registered/spec/dspark/test_ragged_verify_backend_capability.py
cd test/ && python3 registered/unit/spec/test_dspark_step_inputs_once.py
cd test/ && python3 registered/unit/speculative/test_dspark_sampling_accept_graph.py

🚀 1-gpu-h100 (1 test): ✅ View workflow run

cd test/ && python3 registered/core/test_basic_sanity_dspark.py

🚀 4-gpu-h100 (1 test): ✅ View workflow run

cd test/ && python3 registered/spec/dspark/test_dspark_dp_spec_prefill_coordination.py

🚀 4-gpu-b200 (3 tests): ✅ View workflow run

cd test/ && python3 registered/spec/dspark/test_dspark_minimax_mxfp8_stacked_kv.py
cd test/ && python3 registered/e2e/models/test_deepseek_v4_flash_fp4_b200.py
cd test/ && python3 registered/e2e/models/test_deepseek_v4_flash_fp4_megamoe_b200.py

🚀 2-gpu-gb300 (4 tests): ❌ View workflow run

cd test/ && python3 registered/e2e/models/test_deepseek_v41_flash_gb300_dp2ep2.py
cd test/ && python3 registered/e2e/models/test_deepseek_v41_flash_gb300_tp2ep2.py
cd test/ && python3 registered/e2e/models/test_deepseek_v41_flash_gb300_encoder_bcg.py
cd test/ && python3 registered/e2e/models/test_deepseek_v41_flash_gb300_trtllm_bounded_bcg.py

🚀 8-gpu-b300 (1 test): ✅ View workflow run

cd test/ && python3 registered/e2e/models/test_deepseek_v41_pd_cp_dspark.py

@hnyls2002
hnyls2002 merged commit a0d4034 into sgl-project:main Oct 27, 2026
109 of 125 checks passed
@kpham-sgl

Copy link
Copy Markdown
Collaborator Author

The four 2-gpu-gb300 files from the /rerun-test above sat queued with no runner, so we ran them on our own GB300 nodes at the PR head (c66657d2dc) and cancelled the queued run. Each file is the CI command, cd test/ && python3 registered/e2e/models/<file>, run inside an image of lmsysorg/sglang:nightly-dev-cu13-20261027-5884e4fe with this branch installed editable. The Rust extensions were rebuilt from the branch.

Test file Result GSM8K MMMU-Pro Draft glue graph
test_deepseek_v41_flash_gb300_dp2ep2.py OK, 2 tests 0.980 0.760 captured, no failure
test_deepseek_v41_flash_gb300_tp2ep2.py OK, 2 tests 0.975 0.785 captured, no failure
test_deepseek_v41_flash_gb300_encoder_bcg.py OK, 2 tests 0.975 0.770 captured, no failure
test_deepseek_v41_flash_gb300_trtllm_bounded_bcg.py OK, 2 tests 0.980 0.750 off by design (trtllm-gen attention)

None of the four logs has Metadata glue-graph capture failed or Cannot copy between CPU and CUDA.

The 8-gpu-b300 file, test_deepseek_v41_pd_cp_dspark.py, is still queued. It carries @unittest.skip("Temporarily disabled: DSV4.1 PD+CP B300 E2E"), so it only reports a skip.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants