Skip to content

[Fix] dLLM: Preserve prompt tails and literal mask tokens across prefix reuse - #39592

Merged
Fridge003 merged 10 commits into
sgl-project:mainfrom
0z5a:dllm-prefix-tail
Oct 19, 2026
Merged

Fridge003 merged 10 commits into
sgl-project:mainfrom
0z5a:dllm-prefix-tail

Conversation

@0z5a

@0z5a 0z5a commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Incremental review: Files changed against main.

dLLM prefix lookup can consume a partial prompt block and its synthetic masks when a cached prompt contains the same literal mask IDs. Literal mask IDs in a real prompt can also be denoised and emitted as generated tokens, causing KV accounting errors and a serving crash.

Bound cache lookup by complete real-token blocks and classify the request after matching its cache prefix. Carry an explicit committed-position mask into LowConfidence, JointThreshold and JointThresholdInDel so literal prompt IDs and previously emitted outputs stay immutable after retraction. Repeated denoising no longer subtracts the current block from the reported cached-token count. The change preserves autoregressive lookup and logprob boundaries.

The GPU/HTTP results below are historical qualification of the previous revision; they were not rerun for the October 10 refresh.

Test Before After
Real RadixCache collision, five prompt tokens with block size 4 Matches 8 tokens and skips the tail Matches 4; tail preserved
SDAR HTTP prompt containing a literal mask in a full block Service exits with KV pool overcount on baseline and initial lookup-only patch Cold/hot requests complete; prompt remains intact
Prefix boundary, FDFO KV reuse, and literal-prompt algorithm regressions Original boundary matrix: 122 failures out of 210 11 tests and 228 subtests pass
LowConfidence, FDFO, radix enabled — 8 prompt cases plus synthetic-mask collision pass
LowConfidence, explicitly synchronous (--no-dllm-fdfo) — Same 9 cases pass; exact output IDs/text match FDFO
LowConfidence, FDFO, radix disabled — Same 9 cases pass; exact output IDs/text match cache-enabled run
JointThreshold, FDFO, radix enabled — Same 9 cases pass; cold/hot IDs/text identical
JointThreshold, explicitly synchronous — Same 9 cases pass; exact IDs/text match JointThreshold FDFO
HTTP cached-token accounting Negative counts during repeated denoising Nonnegative and equal to the first traced prefix match

HTTP tests use JetLM/SDAR-1.7B-Chat-b32, BF16, TP=1, Triton, CUDA graphs disabled, block size 32, threshold 0.95, greedy sampling, 32 new tokens, and ignore_eos=true. Hardware: RTX PRO 4000 Blackwell 24 GB; Python 3.12, PyTorch 2.13.0+cu130, sglang-kernel 0.4.7. Each configuration runs 19 actual /generate requests. An observation-only wrapper records cache matches after Req.init_next_round_input.

Prompt length Expected hot cache hit Radix-disabled hit
31 0 0
32 32 0
33 32 0
63 32 0
64 64 0
65 64 0
129 128 0
65, including literal mask ID 64 0

The collision test first caches a 33-token prompt followed by 31 literal mask IDs, then submits the original 33-token prompt. Its output matches a clean-cache request and only 32 tokens are reused. Unit tests additionally exercise both JointThreshold implementations and both scheduling modes. These results do not qualify CUDA graph execution or multi-block prefill.

PYTHONPATH=python python -m pytest -q \
  test/registered/unit/mem_cache/test_dllm_fdfo_kv_reuse.py \
  test/registered/unit/dllm/algorithm/test_joint_threshold_indel.py \
  test/registered/unit/dllm/test_gemma4_renoise.py

Contributes to the prefix-cache / prompt-tail item in #39499. The post-cache phase ordering overlaps the prerequisite in #31586; this PR does not add its multi-block scheduler.


CI States

Latest PR Test (Base): ✅ Run #39056280279
Latest PR Test (Extra): ❌ Run #39056280293
Latest PR Test (AMD ROCm 10): ❌ Run #39056280358

October 10 review follow-up

Merged main c55790d38ba0c41170cec8993e7a85d5c687149c and addressed the remaining review items:

  • Batch CPU-known committed lengths into the same pinned, nonblocking transfer as positions. LowConfidence uses torch.where to exclude committed positions. Algorithms read the declared ForwardBatch field directly, and test doubles provide it explicitly.
  • Retraction phase selection and position masks include both prompt tokens and committed outputs, independently of KV residency.
  • JointThresholdInDel uses the explicit immutable mask for state initialization and excludes those positions from mask counts and transfers, while retaining its contiguous-prefix fallback when metadata is absent.
  • Existing registered CPU test files now contain regressions for a literal-mask cache collision, immutable literal prompts in synchronous/FDFO decoding, retracted outputs, repeated-pass cached-token accounting and InDel behavior.

All scoped pre-commit checks passed, including CI registry validation. CPU source execution passed 25 exact repository algorithm tests, committed-token phase/prefix controls over 72 prompt/output layouts, three actual position-branch controls and the repeated-pass cache-accounting guard. Negative controls reproduced the literal-prompt InDel failure; these tests also caught and corrected its mask-count/transfer path. This source-level run omits package initialization and native dependencies. Full registered-suite and HTTP reruns require the complete SGLang environment and were not executed in this refresh; no GPU performance or memory measurement is claimed.

Review fix commit: 0fbe7e46218ce8b728c72095f1332c3b98e6de19.

@0z5a 0z5a changed the title [dLLM] Bound prefix lookup by complete real-token blocks [dLLM] Preserve prompt tails and literal mask tokens across prefix reuse Sep 15, 2026
@0z5a
0z5a marked this pull request as ready for review September 16, 2026 11:50
@0z5a
0z5a marked this pull request as draft September 18, 2026 18:29
@0z5a
0z5a marked this pull request as ready for review September 18, 2026 18:34
@0z5a
0z5a marked this pull request as draft September 18, 2026 23:25
@0z5a
0z5a marked this pull request as ready for review September 19, 2026 07:52
@0z5a
0z5a marked this pull request as draft September 19, 2026 15:05
0z5a added a commit to 0z5a/sglang that referenced this pull request Sep 23, 2026
0z5a added a commit to 0z5a/sglang that referenced this pull request Sep 24, 2026
@rwang5203
rwang5203 marked this pull request as ready for review October 9, 2026 22:20
@rwang5203

Copy link
Copy Markdown
Collaborator

On current main, a prompt holding the mask id still kills the scheduler under JointThresholdInDel, which ignores dllm_prompt_mask. Consider taking its prompt positions and original masks from that mask when it is set.

@rwang5203

Copy link
Copy Markdown
Collaborator

With the regression tests removed, no CI test fails on main for the literal prompt mask, the prefix bound, or negative cached tokens. Could you add one case for each to the existing dLLM tests?

Comment thread python/sglang/srt/dllm/mixin/req.py Resolved
@rwang5203

Copy link
Copy Markdown
Collaborator

Thanks, folding the prompt lengths into the existing transfer removes the sync. Could we build dllm_prompt_mask only for batches where a request’s last partial prompt block holds the mask id, so ordinary batches skip the extra bytes and kernel?

Every denoise pass carried the committed lengths in a pinned staging
buffer and ran a comparison kernel to build dllm_prompt_mask, although
only requests whose committed tokens contain the mask ID need it.

Record per request whether its prompt or kept outputs hold the mask ID,
computed once when its fill IDs are first built and again after
retraction. Batches without such a request keep the plain position
transfer and leave dllm_prompt_mask unset, where every mask ID in the
block is still to be denoised and the algorithms' existing fallbacks
give the same prompt positions.
@rwang5203 rwang5203 added the run-ci CI: run the baseline test suite on this PR label Oct 14, 2026
@rwang5203 rwang5203 changed the title [dLLM] Preserve prompt tails and literal mask tokens across prefix reuse [Fix] dLLM: Preserve prompt tails and literal mask tokens across prefix reuse Oct 14, 2026
@rwang5203

Copy link
Copy Markdown
Collaborator

/rerun-failed-ci

1 similar comment
@rwang5203

Copy link
Copy Markdown
Collaborator

/rerun-failed-ci

@rwang5203 rwang5203 added the bypass-fail-fast CI: a failing job no longer aborts its siblings (lint still gates) label Oct 17, 2026
@rwang5203

Copy link
Copy Markdown
Collaborator

/rerun-failed-ci

@Fridge003
Fridge003 merged commit 80ea5ee into sgl-project:main Oct 19, 2026
176 of 194 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bypass-fail-fast CI: a failing job no longer aborts its siblings (lint still gates) run-ci CI: run the baseline test suite on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants