[HiCache] Rework the buffer-mode storage prefetch pipeline and retry bookkeeping - #39283
xiezhq-hermann wants to merge 2 commits into
Conversation
|
/tag-and-rerun-ci |
|
Local validation (GB300 devserver, prod venv):
|
722bd14 to
3a77d40
Compare
|
Rebased onto current
Re-validated locally on the rebased tree: |
|
/tag-and-rerun-ci |
|
Rebased onto current Second commit fixes two CI failures from the first run:
Re-validated locally on the rebased tree: shared cache suite under the Python core and the Rust core both OK, |
2ff2565 to
31c7a56
Compare
|
/tag-and-rerun-ci |
…bookkeeping Buffer-mode (host memory as transport staging, no L2 tier) storage prefetch used to decide a staged hit's fate inside the prefill adder, after admission had already charged host-hit tokens that a shrunk device prefix could no longer splice. This reworks the pipeline so the L3 hit is planned before selection and materialized only once, and makes the retry path bounded. Scheduler / prefill adder - `PrefillAdder.add_one_req` now selects a prefill shape without allocating (`_select_prefill_admission`), materializes host hits under the prefix lock via `init_load_back`, and commits the admission afterwards (`_commit_prefill_admission`). `init_load_back` may return None to ask for another admission attempt; over-delivered FULL tokens shrink the planned extend range instead of re-selecting, and under-delivery re-selects with no host hit. - The SWA budget gate lives in `_swa_admission_gate`, keeping the ring-slot exact-fit comparison for the unified-KV SWA ring. - Storage prefetch retries move into `StoragePrefetchRetries` (`mem_cache/storage_prefetch.py`): paced miss polls, immediate re-issues for known hits whose anchor moved, and a per-request attempt cap (`--hicache-storage-prefetch-retry-max-attempts`, default 8; the poll interval default becomes 8 passes). Requests past the cap are admitted with whatever the device holds. - HiCache event draining is factored into `_process_hicache_events` and called from the PD-prefill and PP scheduler loops, which had stopped draining prefetch/backup events. Buffer-mode pipeline - Staged holds are anchored by a FULL-only pin (`match_full_device_prefix`, `inc_full_pin`, `dec_full_pin` on the tree core, Python and Rust) so the device prefix under a parked fetch cannot be evicted while the SWA segment lock stays untouched. - Aux components size their prefetch staging in `prepare_prefetch` (`PreparePrefetchResult.staging_tokens`) and allocate it once the hit is known (`alloc_prefetch_staging`), instead of pre-allocating per query. The Rust components follow the same contract: PREFETCH builds take `staging_tokens` and carry no host buffer. - Admission-time device-capacity deferrals are bounded (`max_staged_admission_defers = 32`); past the cap the hold is dropped and the request recomputes. - Parked hits keep their turn ahead of newer hits, stale prefetch acks are ignored once a request re-issued its query, misses are accounted from the published hit count, and EAGLE bigram keys keep their boundary token when the prefetch span is trimmed or staged. - `swa_transient_size` reports SWA slots owned by staged holds so the scheduler invariant checker can account for them. Tests cover the new adder flow, the retry bookkeeping, the staged-hold lifecycle, the scheduler event draining, and the Rust tree-core pin.
…per-fixture atexit pin - `test_swa_prefetch_commit_end_to_end` still built the SWA PREFETCH transfer from pre-allocated host indices; the build now carries the planned staging as placeholder keys (`staging_tokens`) and attaches the host buffer once the hit is known, and returns None when the pool takes no part. Assert the new contract on both sides. - `UnifiedRadixCache.init_hicache` registers `cache.shutdown` with atexit, which keeps every HiCache fixture's device and host pools alive until process exit. The shared cache suite builds hundreds of them, so the 1-GPU unit shard grew to the 32 GiB card limit and OOMed. Unregister the hook per fixture via addCleanup; peak device memory for the suite drops from ~33 GiB to ~7 GiB.
31c7a56 to
9aa0364
Compare
Motivation
Buffer-mode HiCache (host memory used as transport staging, no L2 tier) decided a staged L3 hit's fate inside the prefill adder, after admission had already charged host-hit tokens that a shrunk device prefix could no longer splice. Under load this produced phantom host-hit accounting, holds that wedged admission, and unbounded storage-prefetch retries. PD-prefill and PP schedulers had also stopped draining HiCache events after a scheduler refactor.
Modifications
Prefill adder / scheduler
PrefillAdder.add_one_reqselects a prefill shape without allocating (_select_prefill_admission), materializes host hits under the prefix lock viainit_load_back, then commits (_commit_prefill_admission).init_load_backmay returnNoneto request another admission attempt; FULL over-delivery shrinks the planned extend range, under-delivery re-selects with no host hit._swa_admission_gateand keeps the ring-slot exact-fit comparison for the unified-KV SWA ring.StoragePrefetchRetries(mem_cache/storage_prefetch.py): paced miss polls, immediate re-issues for known hits whose anchor moved, and a per-request cap via--hicache-storage-prefetch-retry-max-attempts(default 8;--hicache-storage-prefetch-retry-poll-intervaldefault becomes 8). Requests past the cap are admitted with whatever the device holds._process_hicache_eventsand called from the PD-prefill and PP scheduler loops.Buffer-mode pipeline
match_full_device_prefix,inc_full_pin,dec_full_pinon the Python and Rust tree cores), so the device prefix under a parked fetch cannot be evicted while the SWA segment lock is left alone.prepare_prefetch(PreparePrefetchResult.staging_tokens) and allocate it once the hit is known (alloc_prefetch_staging).max_staged_admission_defers = 32); past the cap the hold is dropped and the request recomputes.swa_transient_sizeexposes SWA slots owned by staged holds to the scheduler invariant checker.Adaptations to upstream
_swa_admission_gateincorporates the_swa_req_ringexact-fit comparison added upstream in [AMD][DSV4] Reland unified-KV pool sizing and SWA ring accounting, fully gated #38192.arg_groups/fields/memory.py(upstream moved the field declarations there).shrunk).match_full_device_prefixwalks the full radix namespace (extra key + cache salt) likematch_prefixdoes upstream.staging_tokenscontract as the Python components: SWA/MambaPREFETCHbuilds size their placeholder keys from the planned staging and carry no host buffer, which the Python pipeline attaches once the hit is known (alloc_prefetch_staging). Without this the Rust adapter rejected the new keyword and the Rust-backed shared suite failed.swa_tombstone_rangesandattach_swa_window, which have no Rust port yet. The Rust adapter exposes them as explicitNotImplementedErrorstubs (same pattern as itsroot_node), and the three shared-suite tests that exercise that path (test_buffer_only_load_back_uses_full_behind_swa_tombstone,test_buffer_only_load_back_reuses_partial_masked_full,test_buffer_only_load_back_trims_head_published_by_sibling) are skipped under the Rust backend. The last one passed under Rust before this PR, so this is a deliberate coverage regression for buffer-only + SWA + L3 on the Rust core until the helpers are ported; the Python core is unaffected.Original commits
0e78bbe270bdc1fb698557b0a7f139Accuracy Tests
N/A (scheduler / cache bookkeeping change; no numerics affected).
Benchmarking and Profiling
Validated on an internal buffer-mode + L3 setup: warm-cache prefix hit rate no longer collapses under high concurrency and no admission wedges were observed. No absolute numbers are included here.
Checklist
Local validation (details in the first comment): Rust
cargo test --locked(865 passed incl. the new pin test),cargo check --features python-extension,inspection,cargo fmt --check,cargo clippy -D warnings; Python unit suitestest_unified_radix_cache_unittest.py,test_rust_unified_radix_cache_unittest.py,test_storage_prefetch_lifecycle.py,test_prefill_adder.py,test_scheduler_hicache_events.py,test_buffer_mode_sidecar.py,test_hicache_staged_write_back_dispatch.py,test_rust_tree_core.py,test_tree_core_registry.py;ruff,ruff format,isort,codespellon changed files.CI States
Latest PR Test (Base): 🚫 Run #34939088034
Latest PR Test (Extra): ❌ Run #34939087207
Latest PR Test (AMD ROCm 10): ⏳ Run #34939087808