Repository navigation
[Perf] [NVIDIA] dflash: Top up decode KV pages for the whole batch on allocation steps - #47685
Open
weireweire wants to merge 2 commits into
Open
weireweire wants to merge 2 commits into
weireweire wants to merge 2 commits into
Conversation
… step Root cause: DFlashDraftInputV2.prepare_for_decode allocates KV pages only for requests whose committed length plus the 2-block reserve crosses their allocated length. With page 256 and accept ~3.5, each request crosses a page about every 73 steps; in DEP32 c5768 nsys a rank (~36 running) allocated on ~35% of steps. Each allocation step costs ~0.78 ms of host time (get_last_loc 95 us + paged alloc_extend 578 us + assign 56 us). Across 32 DP ranks some rank pays it on nearly every step, before the DP agreement, so it holds the whole step. Fix: SGLANG_OPT_DFLASH_DECODE_TOPUP_PAGES=K (default 0, off; capped at 8). When a step must allocate anyway, every request below K pages of headroom is topped up to K pages beyond its page-aligned need in the same alloc_for_spec_decode call (bounded by the req_to_token row width). Already-running requests then next need pages ~K pages of decode later, and a rank's allocations become shared steps instead of one per request page crossing. The top-up is skipped unless the allocator's free tokens cover the paged-extend eviction demand (needed + top-up + bs pages) plus 3 x the top-up, so it never triggers a prefix-cache eviction the step would not have done anyway. It is skipped under HiSparse, whose spec decode path always evicts and sizes its host pool by kv_allocated_len. Admission becomes slightly more conservative: decode admission reserves decode headroom per running request on top of the already-allocated top-up, so up to K pages per running request are counted twice. Validation (GB300, DSV4.1-Flash, K=2, on a 2026-10-30 nightly, stacked on the padded-metadata-glue / persistent-DP-meta / bulk-prepare host changes; the GPU runs used the guard without the bs-pages term): - Consistency check (diagnostic overlay): every newly allocated [cur, nxt) range unique, nonzero and matching kv_allocated_len, 870 checks / 0 bad; GSM8K 1P1D with real acceptance 96.82%. - DEP32 c5768, isolated (the three host changes in both arms): decode step p50 13.9 -> 13.7 ms; steady-window per-GPU throughput (mean of minutes 8-20, x0.96) 687.8k vs 660.6k tok/s/GPU at p90 interactivity 223.4 vs 219.8 tok/s/user (single pair; both runs have one dip bin). - 9P DEP16 bs192 c7480, all four host changes vs none (not isolated): step p50 0.2-0.26 ms lower per running bucket; throughput neutral (1127.2k vs 1127.2k tok/s/GPU); full KV usage max 0.54 vs 0.55, SWA usage mean 0.112 vs 0.063, 0 retractions. The low-free skip path is not exercised at these loads (GSM8K 1P1D: free tokens >= 15.8x the skip bound on every allocation step). - Existing DFlash / decode-prep unit tests pass on CPU with K=0 and K=2.
This was referenced Nov 2, 2026
SGLANG_OPT_DFLASH_DECODE_TOPUP_PAGES now defaults to 2 instead of 0. The top-up already skips itself when free pages run low (so it never causes an eviction the step would not do anyway) and under HiSparse; the HiSparse warning now fires only when the env is set explicitly.
Contributor
nvpohanh
reviewed
Nov 4, 2026
nvpohanh
left a comment
Collaborator
There was a problem hiding this comment.
[by Claude Code] This review is done by agents. Please feel free to disagree if they do not make sense.
nvpohanh
approved these changes
Nov 4, 2026
Collaborator
|
@Qiaolin-Yu could you review this? thanks |
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
DFlashDraftInputV2.prepare_for_decodeallocates KV pages only for the requests whose committed length plus the 2-block reserve crosses their allocated length. With page size 256 and accept length ~3.5, each request crosses a page about every 73 decode steps. In a DEP32 c5768 nsys profile (~36 running per rank), a rank allocated on ~35% of its steps, and each allocation step cost ~0.78 ms of host time, mostlyget_last_loc(95 us), pagedalloc_extend(578 us) and thereq_to_tokenassign (56 us).Under attention DP this sits before the DP agreement. With 32 ranks allocating independently, some rank pays it on nearly every step, and the whole step waits for that rank.
Modifications
SGLANG_OPT_DFLASH_DECODE_TOPUP_PAGES=K(default 2; 0 turns it off; capped at 8 pages) changes what happens on a step that must allocate anyway:alloc_for_spec_decodecall.req_to_tokenrow width.needed + top-up + bs x page_size, the demandtake_extendpasses tomake_room) plus 3x the top-up. When it is skipped, the step allocates exactly as before. So on the full/SWA token pools the top-up does not trigger a prefix-cache eviction that the step would not have done anyway. (The NPU DSV4 C128 sidecar pool is not part of this check; this path was not run on NPU.)kv_allocated_len.Admission becomes slightly more conservative. Decode admission subtracts a per-running-request decode reservation from the free size, and the free size already excludes the topped-up pages. So up to K pages per running request are counted twice. On a PD decode worker the reservation is
--num-reserved-decode-tokens(512), so at page size 256 and K=2 the worst case doubles it (512 more tokens per running request; 98k tokens at 192 running). This is self-limiting: the guard caps a top-up at a quarter of the free tokens beyondneeded + bs x page_size, so as free tokens approach the reservation the top-ups shrink and stop, and decode uses up the pages already held.Behavior change: the top-up is on by default (K=2) for every DFlash-family decode with a page size above 1. K=2 is the only value measured; it holds at most 2 extra pages per running request, and the free-space guard above makes it fall back to exact allocation when memory is tight.
The change is independent of #47676, #47677 and #47678 (the other DSV4.1 decode host-path changes). It was measured stacked on them.
Accuracy Tests
All runs: GB300, DSV4.1-Flash, K=2, on a 2026-10-30 nightly.
[cur, nxt)range ofreq_to_tokenis nonzero, is not shared with any other request's new range, and thatkv_allocated_lenequalsnxt. Result: 870 checks, 0 bad.Speed Tests and Profiling
DEP32 c5768 (10P1D). This comparison isolates the top-up: both arms have #47676-#47678, and the arm adds
SGLANG_OPT_DFLASH_DECODE_TOPUP_PAGES=2.The throughput gain (+4.1%) comes from a single pair of separate runs, and each run has one dip bin (control at minutes 10-12, arm at minutes 14-16). Run-to-run drift on this shape can reach ~3%, so the throughput delta is not conclusive on its own. The step-time reduction (-0.2 ms p50) is the signal.
9P1D DEP16 bs192 c7480 checks a high-batch, KV-heavy shape. It compares all four host changes against none, so it is not an isolated measurement of the top-up:
At these loads the low-free skip path is not exercised. In the GSM8K run the consistency check also logged the free size: on every allocation step, free tokens were at least 15.8x the skip bound. The GPU runs used the guard before the
bs x page_sizeterm was added; the term adds 10k tokens at 40 running and 49k at 192.Checklist
SGLANG_OPT_DFLASH_DECODE_TOPUP_PAGES=2CI States
Latest PR Test (Base): ❌ Run #41405346973
Latest PR Test (Extra): ❌ Run #41405346992
Latest PR Test (AMD ROCm 10): ❌ Run #41405346896
Tip
Base CI only covers the core cases, and its workload may not exercise your change. Use
/rerun-testto run the tests your change touches, or copy this prompt for an agent: