Repository navigation
[Fix] deterministic: Align chunked prefill to the Gated DeltaNet block size - #44076
Open
aarnetalman wants to merge 5 commits into
Open
aarnetalman wants to merge 5 commits into
aarnetalman wants to merge 5 commits into
Conversation
aarnetalman
force-pushed
the
deterministic-gdn-chunk-align
branch
2 times, most recently
from
October 13, 2026 16:57
359bf30 to
a01cfd4
Compare
Gated DeltaNet prefill processes each prefill chunk in 64-token blocks counted from the chunk start. When chunked prefill splits a prompt, the split point depends on what else is in the batch, so a split off the 64-token grid gives a different result than the same prompt run alone. That makes hybrid GDN models (Qwen3-Next, Qwen3.5 and later) not batch-invariant under --enable-deterministic-inference. In deterministic mode, combine the chunked-prefill alignment with the Gated DeltaNet block size for these models, as is already done for DeepSeek DSA. Other models and normal mode are unchanged. Signed-off-by: Aarne Talman <aarne.talman@amd.com>
…tinuations The deterministic chunk alignment floored a chunk's length, so after a cached prefix that ends off the grid the chunk end still missed it, and continuation chunks were not aligned at all. With the radix cache on, Qwen3.8-27B still failed the prefix log-probability check now and then. Align the absolute chunk end on the first chunk and on continuations (a continuation too short to reach the grid keeps its length so the request still makes progress). With chunk sizes at least the alignment, which startup already checks, no request is held back. Signed-off-by: Aarne Talman <aarne.talman@amd.com>
|
Motivation needs a TL;DR - It looks like there are more words in it than in the code changes 😃 |
… grid A cached prefix can end off the alignment grid. Cover both add_one_req paths (exact chunk fill and page rounding): the first chunk must end on the grid, not just have an aligned length.
Contributor
Author
Updated the description. |
aarnetalman
force-pushed
the
deterministic-gdn-chunk-align
branch
from
October 14, 2026 13:19
359bf30 to
24dfdf6
Compare
rkarhila-amd
approved these changes
Oct 14, 2026
rkarhila-amd
left a comment
There was a problem hiding this comment.
(I'm not a real reviewer, but) I don't see any issues in the code beside.
…e alignment once Import CHUNK_SIZE from the Gated DeltaNet chunk kernel instead of repeating 64 in the scheduler, and collect the alignment requirements before taking their LCM, so truncation_align_size is assigned once.
aarnetalman
marked this pull request as ready for review
October 14, 2026 19:47
aarnetalman
requested review from
Ying1123,
hnyls2002,
merrymercy and
xiezhq-hermann
as code owners
October 14, 2026 19:47
4 tasks done
# Conflicts: # python/sglang/srt/managers/schedule_policy.py
4 of 5 tasks
This was referenced Oct 28, 2026
This branch has not been deployed
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
Hybrid Gated DeltaNet models (Qwen3-Next, Qwen3.5, and later) are not batch-invariant under
--enable-deterministic-inferencewhen chunked prefill splits a prompt: the Gated DeltaNet prefill kernels process each chunk in 64-token blocks counted from the chunk's start, and the split point depends on what else is in the batch, so the same prompt can get different block boundaries and slightly different outputs. This PR removes that effect by ending every prefill chunk of these models on an absolute 64-token position in deterministic mode, also after a radix-cache hit and for continuation chunks. No change outside deterministic mode; no measurable speed change.Full attention does not have this problem.
We measured it on MI350X with Qwen3.8-27B. A 3,000-token prompt with
--chunked-prefill-size 2048was sent alone, then together with one other prompt of 100 to 3,500 tokens:In the end-to-end determinism tests this shows up as occasional
prefixandprefix --return-logprobfailures (one or two extra distinct outputs in ~300 samples, and log-probability mismatches).Aligning the chunk length was not enough on its own. The existing deterministic alignment rounds the chunk length, so:
With only the 64-token rule, the chunk tests passed but the
prefixtests, whose shared prefixes are 511 and 4,097 tokens long, still returned extra distinct outputs in 4 of 8 runs at default settings. So the second commit aligns the absolute end of every chunk, first and continuation alike; with it, 0 of 10 did.This also matches what the other users of this alignment need: FlashInfer is exact when every chunk starts on its q-tile grid (#43612), and DeepSeek DSA asserts that chunked extends start on its kpool boundaries.
The same kernels run on NVIDIA in deterministic mode (FlashInfer GDN prefill is refused there), so this is not specific to ROCm.
Modifications
Scheduler.init_deterministic_inference_config: for models wherehybrid_gdn_config()is set, combinetruncation_align_sizewith the Gated DeltaNet block size (64) usingmath.lcm, the same way DeepSeek DSA's alignment is combined. So triton attention gets 64, and flashinfer keeps 4096.PrefillAdder.add_one_req: when a request is truncated, roundprefix_len + extend_lendown to the alignment, notextend_lenalone.PrefillAdder.add_chunked_req: taketruncation_align_sizeand align a truncated continuation's end the same way. A continuation too short to reach the next boundary keeps its length, so the request always makes progress. The scheduler passes itstruncation_align_size.test_deterministic_chunk_align.py: a hybrid GDN model gets the 64-token grid; other models and normal mode are unchanged.test_prefill_adder.py:add_one_reqpaths (exact chunk fill and page rounding). Aligning only the length would end it at 292.truncation_align_sizeisNonethere), or for models without Gated DeltaNet layers unless they already use the alignment (FlashInfer, DSA). Mamba2 and KDA models are not covered here.Related:
truncation_align_sizegrid inadd_chunked_req(for mamba checkpoint donation with the extra buffer). It rounds the chunk length; this PR rounds the absolute end, which keeps its grid whenever the chunk starts on it. The two touch the same lines; happy to rebase onto whichever lands first.prefill_chunk_align). It lists "an off-grid radix-cache hit" among the remaining holes; the absolute alignment here closes it for the chunks after the hit. If [Scheduler] Align deterministic flashinfer chunks to the q-tile grid, not the split-KV tile #43612 lands first, the 64-token rule belongs in the hybrid linear-attention backend, as the LCM of the dense child's grid and 64. Happy to rebase either way.Accuracy Tests
MI350X,
rocm/sgl-dev:v0.5.21-rocm724-mi35x-20261012with this change applied,--attention-backend triton --linear-attn-prefill-backend triton --linear-attn-decode-backend triton --enable-deterministic-inference,SGLANG_USE_AITER=0, default chunked prefill and radix cache. #43788 and #34543 were applied as well, so that the remaining failures come from this path.Unit tests (
test_deterministic_chunk_align.py,test_prefill_adder.py, 57 tests): before, the 4 new tests fail (2 + 2); after, 57 passed.Split-point test (a long prompt alone vs with one other prompt,
--chunked-prefill-size 2048 --disable-radix-cache, output token IDs and log-probabilities compared exactly):python3 -m sglang.test.test_deterministic --n-trials 50for each mode, and a bitwise check of 1,275 responses (batch sizes 1-50, greedy) against batch size 1. Forprefix, the number of distinct outputs in each of the 4 prefix groups (~300 samples each; 1 = deterministic; this is what the test returns). With--return-logprobthe test also prints a per-sample log-probability comparison, which does not change its return value; it is shown in the column "log-probs". These runs follow the existing deterministic end-to-end tests (TestDeterministicBase, e.g.test_qwen35_deterministic.py), which are CUDA-only and compare decoded text only; here log-probabilities and the bitwise check are compared as well. Happy to add an AMD end-to-end test if wanted.singleprefixprefix --return-logprobradix_cachep_vs_dfails before and after; this PR does not target it.Speed Tests and Profiling
Same setup as above, Qwen3.8-27B TP1,
python3 -m sglang.benchmark.serving --backend sglang --dataset-name random --random-input-len 1024 --random-output-len 1024 --random-range-ratio 1.0 --num-prompts 256 --max-concurrency 256 --request-rate inf --seed 1 --warmup-requests 8 --flush-cache, median of 2 runs each, then with the GPUs swapped:No measurable change; the difference between the two GPUs is larger than the difference between before and after. (TTFT is long at concurrency 256 because the linear-attention state pool limits how many requests run at once, the same before and after.)
Checklist
Review and Merge Process
/tag-and-rerun-ci,/tag-run-ci-label,/rerun-failed-ciCI States
Latest PR Test (Base): ❌ Run #40674960955
Latest PR Test (Extra): ❌ Run #40674960735
Latest PR Test (AMD ROCm 10): ❌ Run #40674961184