Skip to content

[Feat] KVShard: PD transfer and e2e test - #38473

Merged
hzh0425 merged 11 commits into
sgl-project:mainfrom
Shunkangz:kvshard/4-pd-transfer-e2e
Oct 20, 2026
Merged

hzh0425 merged 11 commits into
sgl-project:mainfrom
Shunkangz:kvshard/4-pd-transfer-e2e

Conversation

@Shunkangz

@Shunkangz Shunkangz commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

The PR that makes the feature work end to end.

A sharded prefill rank holds only its stripe of every cached page, so the transfer changes on both ends: each prefill rank ships the pages it owns, and decode pulls from every rank of the shard group instead of one. Ownership is derived from the value (owner = logical_page % N), not the position, so the filter stays correct under any allocator placement policy — and the wire value is already the owner's local page id, so the decode side needs no change.

Validated PD-disaggregated on both arms: MLA sharded across attention-TP (GSM8K 0.82) and GQA under prefill CP sharded across attention-CP (GSM8K 0.935), both added here as registered E2E tests.

Modifications

Accuracy Tests

Speed Tests and Profiling

Checklist

Review and Merge Process

  1. Ping Merge Oncalls to start the process. See the PR Merge Process.
  2. Get approvals from CODEOWNERS and other reviewers.
  3. Trigger CI tests with comments or contact authorized users to do so.
    • Common commands include /tag-and-rerun-ci, /tag-run-ci-label, /rerun-failed-ci
  4. After green CI and required approvals, ask Merge Oncalls or people with Write permission to merge the PR.

CI States

Latest PR Test (Base): ✅ Run #39209811480
Latest PR Test (Extra): ✅ Run #39209811542
Latest PR Test (AMD ROCm 10): ❌ Run #39209811722

test_page_interleave_shard.py: main (sgl-project#40925) added multi-GPU gather and DSA
indexer tests to the file this branch extends. Keep both: the branch's CPU
tests (TestShardedPoolFootprint, TestShardSendFilter) stay with the other CPU
tests before main's multi-GPU section, the docstring lists CPU items 1-7 then
main's GPU paragraph, and the imports are the union of both sides.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Shunkangz

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

1 similar comment
@Shunkangz

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

_resolve_rank_mapping read info.enable_kv_cache_sharding before checking
is_mla_backend. A non-MLA decode now stops at is_mla_backend, so it reads
only the fields main reads. This fixes the hetero-TP SWA wire test, whose
SimpleNamespace info has no sharding field. Behavior is unchanged for a
real PrefillServerInfo, where the field defaults to False.
@Shunkangz

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

@hzh0425 hzh0425 self-assigned this Oct 15, 2026
@hzh0425 hzh0425 added the bypass-fail-fast CI: a failing job no longer aborts its siblings (lint still gates) label Oct 15, 2026
@hzh0425

hzh0425 commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator

/rerun-failed-ci

@alphabetc1 alphabetc1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

the rest LGTM

AMD-yanfeiwang pushed a commit to AMD-yanfeiwang/sglang that referenced this pull request Oct 18, 2026
Squashed dependency: the net change of sgl-project#38473 (open upstream) relative to
main, squashed into one commit so this PR's own commits stand alone. Credit
for this commit belongs to the sgl-project#38473 author; review it in sgl-project#38473.

A sharded prefill rank holds only its stripe of every KV page, so each rank
sends the pages it owns and decode pulls from every rank of the shard group.

- disaggregation/utils.py: filter_kv_indices_for_shard_rank (logical page l
  lives on rank l % N as local page l // N).
- disaggregation/common/conn.py: filter every send by the shard rank, skip
  empty non-final chunks, advertise enable_kv_cache_sharding through
  bootstrap; decode pulls from all prefill ranks of the shard group.
- disaggregation/prefill.py: widen the bootstrap capacity bound by the
  shard size.
- Read the sharding flag only on the MLA rank-mapping path.
- Tests: send filter, empty-chunk skip, bootstrap payload, per-rank KV
  memory cut, and a 4-GPU PD E2E gating GSM8K accuracy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

@Shunkangz Shunkangz changed the title [kv-shard 4/4] PD transfer and e2e test [Feat] KVShard: PD transfer and e2e test Oct 19, 2026
Shunkangz and others added 3 commits October 18, 2026 23:16
…r fit

The PD bootstrap check rejected only prompts longer than N*T (N shard
ranks, T tokens per rank). Sharded PrefillAdder admission needs
prompt + 1 new token + one page per shard < N*T, strictly, so prompts in
[N*T - N*ps - 1, N*T - 7] passed intake and bootstrap but were refused
with NO_TOKEN on every pass, even on an empty pool. Under FCFS such a
request blocked the prefill queue until decode's 300 s KV-wait timeout
turned it into a 500. With tp=2, page 64 and 16384 tokens per rank, that
is 32639..32761.

The bootstrap check now rejects prompts longer than N*T - N*ps - 2
(32638 there) with a 400. Unsharded runs keep their limit.

Test: a CPU unit test drives the real sharded PrefillAdder on an empty
PageInterleavePoolAllocator and the real bootstrap check at the limit
and one past it, for N in {2, 4}.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sharding assigns whole logical pages to ranks; it does not split every page
into per-rank stripes. Fix the comments and the E2E docstring that said
otherwise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sion path

With the radix cache disabled, PrefillAdder admits ignore_eos requests
through add_one_req_ignore_eos. That path rounds the prompt up to a page and
keeps 1 new token plus IGNORE_EOS_RESERVE_TOKENS spare, so on an empty pool
it admits only ceil_page(L) <= N*T - N*ps - 3. The previous limit,
N*T - N*ps - 2, still let [N*T - (N+1)*ps + 1, N*T - N*ps - 2] wait forever
(32577..32638 for tp=2, page 64, 16384 tokens per rank).

The limit is now floor_page(N*T - N*ps - 3), 32576 there. A sweep over
N in {2, 4, 8} and page sizes {2, 16, 64} matches the largest prompt both
real admission paths admit. The boundary test now also drives the
ignore_eos path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hzh0425

hzh0425 commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator

/rerun-group disaggregation

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

🚀 4-gpu-gb300 (1 test): ✅ View workflow run

cd test/ && python3 registered/disaggregation/test_disaggregation_aarch64.py

🚀 2-gpu-h100 (6 tests): ✅ View workflow run

cd test/ && python3 registered/disaggregation/test_disaggregation_basic.py
cd test/ && python3 registered/disaggregation/test_disaggregation_chunked_prefill_abort.py
cd test/ && python3 registered/disaggregation/test_disaggregation_decode_offload.py
cd test/ && python3 registered/disaggregation/test_disaggregation_optimistic_prefill.py
cd test/ && python3 registered/disaggregation/test_disaggregation_rust_server.py
cd test/ && python3 registered/disaggregation/test_disaggregation_unified_memory.py

🚀 8-gpu-h20 (5 tests): ✅ View workflow run

cd test/ && python3 registered/disaggregation/test_disaggregation_decode_radix_cache.py
cd test/ && python3 registered/disaggregation/test_disaggregation_different_tp.py
cd test/ && python3 registered/disaggregation/test_disaggregation_dp_attention.py
cd test/ && python3 registered/disaggregation/test_disaggregation_nixl.py
cd test/ && python3 registered/disaggregation/test_disaggregation_pp.py

🚀 8-gpu-h200 (4 tests): ✅ View workflow run

cd test/ && python3 registered/disaggregation/test_disaggregation_decode_radix_cache_swa.py
cd test/ && python3 registered/disaggregation/test_disaggregation_dsv4.py
cd test/ && python3 registered/disaggregation/test_disaggregation_hisparse.py
cd test/ && python3 registered/disaggregation/test_disaggregation_hybrid_attention.py

🚀 4-gpu-b200 (2 tests): ✅ View workflow run

cd test/ && python3 registered/disaggregation/test_disaggregation_dwdp_gpt_oss.py
cd test/ && python3 registered/disaggregation/test_disaggregation_inkling_mxfp8.py

🚀 4-gpu-h100 (2 tests): ✅ View workflow run

cd test/ && python3 registered/disaggregation/test_disaggregation_kimi_linear.py
cd test/ && python3 registered/disaggregation/test_epd_disaggregation.py

⛔ registered/disaggregation/test_disaggregation_xpu.py: test/registered/disaggregation/test_disaggregation_xpu.py is registered for XPU (suite stage-b-test-1-gpu-xpu), not for CUDA or CPU; rerun-test.yml has no XPU job. Rerun it with /rerun-failed-ci, or dispatch the XPU workflow manually.

AMD-yanfeiwang pushed a commit to AMD-yanfeiwang/sglang that referenced this pull request Oct 20, 2026
Squashed dependency: the net change of sgl-project#38473 (open upstream) relative to
main, squashed into one commit so this PR's own commits stand alone. Credit
for this commit belongs to the sgl-project#38473 author; review it in sgl-project#38473.

Each sharded prefill rank owns a subset of logical KV pages, so each rank
sends the pages it owns and decode pulls from every rank of the shard group.

- disaggregation/utils.py: filter_kv_indices_for_shard_rank (logical page l
  lives on rank l % N as local page l // N).
- disaggregation/common/conn.py: filter every send by the shard rank, skip
  empty non-final chunks, advertise enable_kv_cache_sharding through
  bootstrap; decode pulls from all prefill ranks of the shard group.
- disaggregation/prefill.py: widen the bootstrap capacity bound by the
  shard size, and reject at bootstrap any prompt that sharded prefill
  admission can never fit (bounded by the stricter ignore_eos path).
- Read the sharding flag only on the MLA rank-mapping path.
- Tests: send filter, empty-chunk skip, bootstrap payload, bootstrap limit
  vs sharded admission boundary, per-rank KV memory cut, and a 4-GPU PD
  E2E gating GSM8K accuracy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@ShangmingCai ShangmingCai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

All changes are correct and safe, LGTM

@hzh0425
hzh0425 merged commit eb4b167 into sgl-project:main Oct 20, 2026
295 of 312 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blackwell SM100/SM120 bypass-fail-fast CI: a failing job no longer aborts its siblings (lint still gates) memory-pool run-ci CI: run the baseline test suite on this PR run-ci-extra CI: also run the extra suite (requires run-ci) unified-radix-cache

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants