Skip to content

[Bugfix][HiSparse][NIXL] Import full blocks without tail prefill on D - #57049

Merged
LucasWilkinson merged 7 commits into
vllm-project:mainfrom
NickLucche:fix/hisparse-full-block-import
Sep 17, 2026
Merged

LucasWilkinson merged 7 commits into
vllm-project:mainfrom
NickLucche:fix/hisparse-full-block-import

Conversation

@NickLucche

@NickLucche NickLucche commented Sep 15, 2026 •

Copy link
Copy Markdown
Member

Purpose

HiSparse currently rejects host imports whose computed-token count is not block aligned. This draft explores an alternative to #56946: receive the full final block and restore its contents into one writable GPU page, so a successful NIXL full-prompt import resumes with a single-token forward instead of prefilling the unaligned tail on D.

The idea is that we want to avoid having to run prefill on D, however short they might be (<block_size here), with north star being deploying D with decode-only CG (aligned with rest of cases).

NIXL analogy: transfer capacity is not sequence length

NIXL already transfers physical blocks containing partially filled prompt tails. Moving the unused bytes does not make them computed tokens. HiSparse can use the same distinction:

For a 32,019-token prompt, block size 128 This approach
Real imported tokens 32,019
Host blocks received 251
Physical capacity 32,128 token slots
Unused tail capacity ("holes") 109 token slots
Prompt work on D after a full import The usual last-token replay for logits

The holes are unused rows within the request's final host block. They do not change token positions or sequence length, and generation fills them at their normal offsets. P does not compute dummy padding tokens. No sub-block allocator or NIXL wire-protocol change is required.

Implementation and tradeoff

  • Reserve one writable resident page for the final imported block, including aligned prompts whose last token is replayed.
  • After a successful external receive, enqueue a full-page host-to-device restore on every TP rank. Copies run on the compute stream before the forward/CUDA-graph replay.
  • Retain both endpoints until every participating worker completes its copy. Avoid publishing an uninitialized resident copy to prefix-cache users, and keep an incomplete host page unsealed.
  • Register a completed, valid restored GPU page for later prefix hits only after all workers finish. Pending restores remain hidden, and incomplete pages and aborted requests are excluded. This incorporates NickLucche/vllm#12.
  • Continue using the existing row-mirror and page-sealing paths for newly generated KV.

Compared with #56946, this transfers the final block and pays for one page of H2D copying per layer/rank rather than recomputing up to block_size - 1 prompt tokens. It also reserves a resident tail page for aligned imports. The unused host rows are ordinary tail capacity, not extra logical tokens or permanent gaps between prompt and output. No performance improvement has been measured.

The intended benefit is to keep successful full-prompt P/D handoffs on single-token execution, compatible with decode-only graph capture. This is not a guarantee that D can never prefill: cache misses, failed transfers, preemption, or partial external hits can still require recomputation.

Related work

Checked #56699 and its comments, open PRs mentioning it, and open PRs matching HiSparse import, landing, tail, partial, full block, and tail restore.

Related to the deployment discussion in #56699; this does not claim to fix that issue's CUDA launch failure or establish end-to-end P/D correctness.

Test Plan

Exercise aligned, unaligned, and sub-block imports; last-token resumption; partial-page publication; multi-worker completion and abort lifetime; and tail-copy contents on both host-writer and non-writer ranks.

Before marking ready, run real NIXL P/D on NVIDIA GPUs with decode-only graphs, check generated-token/logprob parity against the prefiller reference, and exercise page-boundary crossing, eviction/preemption, and concurrent traffic. Heterogeneous TP/DCP coverage remains pending.

Test Result

.venv/bin/python -m pytest \
  tests/v1/core/test_prefix_caching.py \
  tests/v1/kv_connector/unit/test_hisparse_connector.py \
  tests/v1/worker/test_utils.py \
  -k 'hisparse or connector_completes_partial_prefix' -q

99 passed, 2 skipped, 117 deselected on macOS. The two skipped cases require CUDA. Commit-time pre-commit hooks passed, including the configured mypy hook.

Rerun after integrating #12 and rebasing onto upstream/main at fc8132a5e5: 99 passed, 2 skipped, 117 deselected. The existing import test now checks that one worker completion does not publish the GPU copy and that full completion publishes aligned tails. Pre-commit hooks passed across all PR files. GPU/model validation of the combined change remains pending.

Additional existing empty-receive checks after restoring those two NIXL hunks:

.venv/bin/python -m pytest tests/v1/kv_connector/unit/test_nixl_connector.py \
  -k empty_recv_is_reported_only_when_awaited -q

4 passed, 130 deselected on macOS. The separate completion-time positional group-pairing workaround reported by the GPU investigation is not included in this draft.

After rebasing onto upstream/main (c6fa1f05d1, including #57027), fixed the H200 CI failure in test_hisparse_spill_and_prefix_restore[offload]: the sync checker reports the tail copy as potentially synchronous because PyTorch does not recognize the host pool's external cudaHostRegister registration. A narrow gpu_sync_allowed() scope covers that copy; the transfer and stream ordering are unchanged. The existing CUDA tail-restore test now allocates the host pool through the production registration path and enables strict sync checking.

.venv/bin/python -m pytest tests/v1/worker/test_utils.py \
  tests/v1/core/test_prefix_caching.py -k hisparse -q
.venv/bin/python -m pytest tests/v1/kv_connector/unit/test_nixl_connector.py \
  -k empty_recv_is_reported_only_when_awaited -q

Post-rebase results on macOS: 88 passed, 2 CUDA cases skipped and 4 passed, respectively. Pre-commit hooks passed across all PR files. The CUDA regression and failing GPU e2e test still require the new CI run.

Model evaluation / GPU serving: not run. This is a draft for design review, published at the submitter's explicit request without waiting for GPU testing. No accuracy, graph-replay, throughput, or latency claim is made.

AI assistance (OpenAI Codex) was used for implementation, tests, and this description. Human line-by-line review and human-run GPU validation remain pending before this draft is ready for merge.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@NickLucche

Copy link
Copy Markdown
Member Author

/ci run

@github-actions

Copy link
Copy Markdown

❌ This PR is 4 commits behind upstream main. Your branch must contain every commit currently on upstream main. No new CI build was started. Merge or rebase onto the latest main, then rerun /ci run. To test this branch at your own risk, use /ci run --allow-stale.

@NickLucche

Copy link
Copy Markdown
Member Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #89167 for commit 29e3748ffdd8.

@NickLucche NickLucche changed the title [Draft][HiSparse][NIXL] Import full blocks without tail prefill on D [HiSparse][NIXL] Import full blocks without tail prefill on D Sep 15, 2026

@LucasWilkinson LucasWilkinson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me thanks for doing this! minor issue Astra found: NickLucche#12

@NickLucche

Copy link
Copy Markdown
Member Author

/ci run

@LucasWilkinson LucasWilkinson changed the title [HiSparse][NIXL] Import full blocks without tail prefill on D [Bugfix][HiSparse][NIXL] Import full blocks without tail prefill on D Sep 16, 2026
@LucasWilkinson

Copy link
Copy Markdown
Contributor

/ci run

@LucasWilkinson LucasWilkinson added this to the v0.30.0 cherry picks milestone Sep 16, 2026
@github-actions

Copy link
Copy Markdown

❌ This PR is 12 commits behind upstream main. Your branch must contain every commit currently on upstream main. No new CI build was started. Merge or rebase onto the latest main, then rerun /ci run. To test this branch at your own risk, use /ci run --allow-stale.

@mergify mergify Bot added the bug Something isn't working label Sep 16, 2026
@LucasWilkinson

Copy link
Copy Markdown
Contributor

/ci run

@NickLucche

Copy link
Copy Markdown
Member Author

/ci retry

@github-actions

Copy link
Copy Markdown

✅ Queued 5 failed job(s) for retry in Buildkite CI #89355.

@NickLucche
NickLucche force-pushed the fix/hisparse-full-block-import branch from fcea4f6 to 33b976a Compare September 16, 2026 18:12
@NickLucche

Copy link
Copy Markdown
Member Author

/ci run

@NickLucche

Copy link
Copy Markdown
Member Author

/ci retry

@github-actions

Copy link
Copy Markdown

✅ The previous CI build is still running: https://buildkite.com/vllm/ci/builds/89355

@NickLucche

Copy link
Copy Markdown
Member Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #89443 for commit e0ec0db4943f.

@khluu

khluu commented Sep 17, 2026

Copy link
Copy Markdown
Member

/ci retry

@github-actions

Copy link
Copy Markdown

✅ Queued 1 failed job(s) for retry in Buildkite CI #89443.

NickLucche and others added 7 commits September 17, 2026 15:09
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: NickLucche <nicolo.lucchesi@mistral.ai>
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: NickLucche <nicolo.lucchesi@mistral.ai>
Reuse the notify-only read and empty-region shape hunks from PR vllm-project#56946 (1e57414), originally carried in PR vllm-project#55398. These fixes are independent of the HiSparse import tail policy. Preserve awaiting_kvs completion semantics.

Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: NickLucche <nicolo.lucchesi@mistral.ai>
Co-authored-by: OpenAI Codex <noreply@openai.com>
Signed-off-by: NickLucche <nicolo.lucchesi@mistral.ai>
Use a narrow gpu_sync_allowed scope for the CUDA-registered host pool and exercise the production allocation path in the CUDA restore test.

Co-authored-by: OpenAI Codex <noreply@openai.com>

Signed-off-by: NickLucche <nicolo.lucchesi@mistral.ai>
Register the resident copy after every worker completes the tail restore so later prefix hits can reuse it.

Co-authored-by: OpenAI Codex <noreply@openai.com>

Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
(cherry picked from commit cf502e6)
Signed-off-by: NickLucche <nicolo.lucchesi@mistral.ai>
Co-authored-by: OpenAI Codex <noreply@openai.com>

Signed-off-by: NickLucche <nicolo.lucchesi@mistral.ai>
@NickLucche
NickLucche force-pushed the fix/hisparse-full-block-import branch from e0ec0db to 3d83c84 Compare September 17, 2026 13:11
@NickLucche

Copy link
Copy Markdown
Member Author

/ci retry

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #89629 for commit 3d83c84480e7, running 1 failed step(s) from Buildkite CI #89443.

@LucasWilkinson
LucasWilkinson merged commit 67e5b0a into vllm-project:main Sep 17, 2026
13 checks passed
khluu pushed a commit that referenced this pull request Sep 18, 2026
…#57049)

Signed-off-by: NickLucche <nicolo.lucchesi@mistral.ai>
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
Co-authored-by: OpenAI Codex <noreply@openai.com>
Co-authored-by: Lucas Wilkinson <lwilkins@redhat.com>
LucasWilkinson added a commit that referenced this pull request Sep 24, 2026
Resolve conflicts with #57049 (HiSparse tail restore on host imports) and
#57389 (DCP region pulls): host-path imports keep upstream's writable tail
page, direct GPU landing is unchanged. Pin upstream's tail-restore tests to
the host fallback and adapt tests to the instance-method
_apply_prefix_caching_by_region and the scheduler's hisparse attribute.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
ch2lab added a commit to ch2lab/vllm that referenced this pull request Oct 19, 2026
Conflict resolution (30 files):
- 19 files: adopted v0.32.0 (our matching cherry-picks vllm-project#57554 vllm-project#56545 vllm-project#57871
  vllm-project#53187 vllm-project#57049 vllm-project#57077 vllm-project#57570 are now upstream).
- Re-reverted vllm-project#53007 (attention.py / kv_cache_utils.py / test_backend_per_kind.py):
  page_budget=shared_page, restore _largest_kernel_block_within candidates logic.
- flashinfer.py: adopted upstream SM12x NVFP4 KV (vllm-project#46963/vllm-project#60452); re-applied our
  D256 prefill (_nvfp4_d256_prefill + FIPrefill fields + call sites).
- kernel_warmup.py: adopted upstream autotune refactor; re-applied our W4A16
  CuTe-DSL autotune skip.
- qwen3_dflash.py: adopted upstream quantized-draft KV fallback (dropped our
  NVFP4 fused dequant fast-path; DFlash2 is backup).
- dflash/eagle utils.py: kept our shared-cache cache_config handling (avoid
  replace-copy layout loss) + upstream get_draft_load_config.
- entrypoints: union imports (our per-request logging + upstream additions).
michael-gpu pushed a commit to practical-inference/vllm-ampere-extended that referenced this pull request Oct 20, 2026
…vllm-project#57049)

Signed-off-by: NickLucche <nicolo.lucchesi@mistral.ai>
Signed-off-by: Lucas Wilkinson <lwilkins@redhat.com>
Co-authored-by: OpenAI Codex <noreply@openai.com>
Co-authored-by: Lucas Wilkinson <lwilkins@redhat.com>
@MatthewBonanni MatthewBonanni added the hisparse Related to HiSparse label Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working hisparse Related to HiSparse kv-cache-manager kv-connector

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants