Guard hc_split_sinkhorn against DP attention's empty idle batch - #38952
Merged
hnyls2002 merged 1 commit intoSep 10, 2026
Merged
Conversation
Serving DeepSeek-V4.1-Flash with `--enable-dp-attention` on H200 kills
every idle rank during startup warmup:
tvm.error.InternalError: CUDALaunch Error: grid dimension must be
positive, but got grid=(0,1,1)
__tvm_ffi_hc_split_sinkhorn_kernel_
... eager_runner.py, in _execute_idle
`hc_split_sinkhorn` derives its grid from the token count and has no
empty-batch early-out, so DP attention's idle forward — which carries no
tokens — reaches the kernel with `b * s == 0` and launches a zero-sized
grid. Exactly the ranks with no work die: 7 of 8 at `--tp 8 --dp-size 8`,
6 processes at `--tp 8 --dp-size 4`, and the server never becomes ready.
Both call sites in `models/deepseek_v4.py` call it unconditionally.
Return correctly shaped empty tensors instead. The check sits ahead of
the gfx1250 branch so the Triton port is covered too; for a non-empty
batch it is a no-op.
Verified on 8x H200: `--tp 8 --ep-size 8 --dp-size 8
--enable-dp-attention` now starts, `/server_info` reports `dp_size=8,
enable_dp_attention=true`, and four sequential requests (seven idle ranks
apiece) plus sixteen concurrent ones all answer correctly with the server
staying up. A non-DP run on the same tree is unchanged. 4x GB300 at
`--tp 4 --dp-size 4` already worked before this change and still does.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
zijiexia
requested review from
BBuf,
DarkSharpness,
HaiShaw,
HydraQYH,
celve and
yuan-luo
as code owners
September 10, 2026 22:16
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.
Motivation
Targets the
dsv4.1branch (#38798) rather thanmain, since that is where DeepSeek-V4.1lives today.
Serving
deepseek-ai/DeepSeek-V4.1-Flashwith--enable-dp-attentionon H200 kills everyidle rank during the startup warmup, so the server never becomes ready:
with the Python frames ending in the DP-attention idle path:
Precisely the ranks with no work die — 7 of 8 at
--tp 8 --dp-size 8, and 6 processes at--tp 8 --dp-size 4(3 idle DP groups x 2 TP ranks) — i.e. every rank that is idle while thesingle warmup request is served.
hc_split_sinkhornderives its grid from the token count and has no empty-batch early-out,so a zero-token forward reaches the TileLang kernel and launches a zero-sized grid. Both call
sites in
models/deepseek_v4.py(the two mHC pre-norm paths) call it unconditionally.Ruled out with single-variable runs on the failing host, so the report is not confounded:
--cuda-graph-max-bs-decode 64--attention-backend dsv4/--moe-runner-backend flashinfer_mxfp4dp_size8 vs 44x GB300 at
--tp 4 --ep-size 4 --dp-size 4 --enable-dp-attentionstarts and serves fine, soin practice this bites Hopper: Blackwell appears never to reach the kernel with zero tokens
(its idle batch stays inside a captured CUDA graph) while Hopper takes the eager idle path.
I did not instrument GB300 to prove that, so I would not state it as more than an
observation — the missing guard itself is unconditional, and the fix is not arch-specific.
Modifications
One early-out in
hc_split_sinkhorn, returning correctly shaped empty tensors. It sits aheadof the
is_gfx1250_supported()branch so the Triton port is covered too, and for a non-emptybatch it is a no-op.
Accuracy Tests
Reproduction and fix both on 8x H200, real
deepseek-ai/DeepSeek-V4.1-Flashweights, thisbranch's tree:
/server_infodp_size=8, enable_dp_attention=true, tp_size=815% of 240 is **36**)--tp 4 --dp-size 4No second zero-grid kernel surfaced behind this one; the sinkhorn launch was the only
unguarded site on the mHC idle path.
Environment: H200 (CC 9.0), PyTorch 2.13.0+cu130, sglang-kernel 0.4.6.post1,
flashinfer 0.6.18, triton 3.7.1. Counter-example host: 4x GB300 (CC 10.3), same tree.
Speed Tests and Profiling
Not applicable — the guard only runs for a zero-token batch, which previously crashed.
Checklist
Note
The published
lmsysorg/sglang:dev-dsv41image predates #38804 and still carries the olderguard
"V4.1 vision currently supports TP/EP without DP, CP, PP or MoE A2A", so it rejects--enable-dp-attentionoutright — this crash is only reachable on a build that includes thatcommit, i.e. this branch.
🤖 Generated with Claude Code
CI States
Latest PR Test (Base): ❌ Run #34536638902
Latest PR Test (Extra): ❌ Run #34536638839
Latest PR Test (AMD ROCm 10): ❌ Run #34536638987