Reduce CP HiCache L2 allocator scan cost

Host HiCache reservations were paying token-level free-slot scans when trying to preserve page contiguity. The allocator now keeps a lazy page-extent index so availability checks and contiguous-preferred allocations avoid materializing the full 220GB-equivalent free-slot metadata path.

The companion benchmark models steady-state L2 churn near full occupancy, including burn-in and historical node-size effects, so LPF/RDMA descriptor quality can be separated from ETE noise.

Constraint: CP HiCache host allocations are page-shaped, but existing callers may still read free_slots directly.
Rejected: Sort and scan free_slots on each alloc_contiguous_preferred call | measured ms-level CPU overhead on 220GB-equivalent metadata.
Rejected: Remove free_slots compatibility | storage/tests still rely on the public tensor surface.
Confidence: medium
Scope-risk: moderate
Directive: Do not reintroduce per-allocation full free_slots scans on HostKVCache; preserve page-extent metadata or benchmark before changing allocator shape.
Tested: Local py_compile for memory_pool_host.py, allocator benchmark, and related tests.
Tested: Local test_cp_hicache_allocator_bench.py 10 passed.
Tested: Remote g0034 test_hicache_controller_cp.py 67 passed; test_cp_hicache_allocator_bench.py 10 passed.
Tested: Remote 220GB-equivalent host_churn benchmark: contiguous path reduced from ms-level to ~30-292us p50 depending on fragmentation.
Not-tested: Full CUDA ETE run after allocator change.
Not-tested: Production long-run fragmentation behavior under live traffic.
This commit is contained in:
laoyao0822
2026-06-02 23:35:12 +08:00
parent 7c8fa2f71c
commit 401de0f8ce
5 changed files with 925 additions and 2 deletions

View File

@@ -5311,3 +5311,154 @@ C123 full-suite update:
- The CPU stub now also gives `sgl_kernel.kvcacheio` a module-level
`__getattr__`, so named imports resolve to inert functions without importing
the native extension.
### C124 — 2026-06-02 L2 host churn benchmark must model fragmented steady-state eviction
Finding:
- The earlier host allocator benchmark measured one cold allocation against a
synthetic `free_slots` layout. That is not enough for production HiCache: L2
host cache can be ~220 GB, runs near full occupancy, and repeatedly frees old
nodes before reserving a new node.
- If prefill nodes and measured allocation requests have the same page size, a
random eviction workload can still produce unrealistically contiguous freed
chunks. This hides the allocator/pathology we care about for layer-page-first
(LPF) transfer planning.
Correction:
- Added `benchmark/hicache/bench_cp_hicache_allocator_overhead.py --bench
host_churn` for steady-state L2 metadata churn.
- The benchmark now records both CPU allocation latency and transfer-descriptor
quality proxies:
- `pf_desc`: page-first descriptor count, one descriptor per selected page.
- `lpf_desc_mean` / `lpf_ratio`: layer-page-first descriptor count after
coalescing consecutive physical page runs.
- `run_p50` / `max_run_mean`: selected physical-page run quality.
- Added `--host-churn-prefill-node-pages` so the filled resident set can use a
different historical node size than the new allocation request. This models
cache built from many small chunks, then later serving larger extensions.
Scope:
- This is CPU metadata-only. It does not allocate real 220 GB KV buffers and it
does not test CUDA kernel bandwidth.
- It is intended to decide whether L2 `HostKVCache.alloc_contiguous_preferred()`
/ future L2 bucket allocator work is on the actual hot path, and whether LPF
layout can get enough physical-page coalescing to help H2D/D2H/RDMA.
C124 validation update:
- Local CPU unit coverage: `test_cp_hicache_allocator_bench.py` passes with the
fragmented prefill-node churn case.
- Remote `g0034` container validation: py_compile passed and the benchmark unit
file passed (`9 passed`).
- Remote 220GB-equivalent production `HostKVCache` churn sample
(`pages=34375`, `occ=0.97`, `request_pages=512`, `evict_pages=512`):
- `prefill_node_pages=1`: FIFO p50 ~11 us but LPF run quality is poor
(`lpf_ratio≈0.663`, `run_p50≈505`). Contiguous-preferred p50 ~700 us and
still cannot find a good run under this fragmentation.
- `prefill_node_pages=64`: FIFO p50 ~9 us and LPF run quality is much better
(`lpf_ratio≈0.012`, `run_p50≈8`). Contiguous-preferred p50 ~499 us.
- Interpretation: current production `alloc_contiguous_preferred()` can add
hundreds of microseconds on fragmented 220GB-equivalent metadata. The
benchmark now makes this measurable separately from ETE noise; it also shows
that node-size history strongly affects LPF transfer coalescing potential.
### C125 — 2026-06-02 Host churn benchmark needs explicit burn-in to avoid cold free-tail bias
Finding:
- A high-occupancy prefill still leaves an initial contiguous free tail. With
small `warmup` and small requests, measured allocations can consume this cold
tail before they ever allocate from evicted fragmented nodes.
- That can overstate LPF run quality and understate the allocation/search cost we
expect after the service has churned for a while.
Correction plan:
- Add a separate `--host-churn-burnin` iteration count. Burn-in iterations run
the same evict+allocate cycle but are not measured and are independent of
benchmark warmup.
- Use burn-in to exhaust the initial free tail before collecting steady-state
latency and run-quality samples.
C125 validation update:
- Local RED/GREEN: `test_host_churn_burnin_exposes_fragmented_evicted_nodes_after_cold_tail`
first failed on missing `burnin`, then passed after adding the burn-in path.
- Local full benchmark unit file: `10 passed` plus py_compile.
- Remote `g0034` container: py_compile passed and
`test_cp_hicache_allocator_bench.py` passed (`10 passed`).
- Remote 220GB-equivalent production `HostKVCache` sample with `burnin=20`,
`occ=0.97`, `evict_pages=512`:
- `request_pages=64`, `prefill_node_pages=1`: FIFO p50 ~11 us but LPF quality
is worst case (`lpf_ratio=1.000`, all single-page runs). The contiguous
search can cost ~2.6 ms p50.
- `request_pages=512`, `prefill_node_pages=1`: FIFO p50 ~10 us, LPF quality
remains worst case (`lpf_ratio=1.000`). Contiguous search p50 ~576 us and
cannot improve run quality because no 512-page run exists.
- `prefill_node_pages=64`: LPF quality is much better (`lpf_ratio≈0.0120.031`)
but contiguous search can still cost ~0.51.5 ms p50.
- Interpretation: this benchmark now exposes the relevant tradeoff: FIFO is cheap
but can create very high descriptor count for LPF/RDMA when historical nodes
are tiny; naive contiguous search can be milliseconds on 220GB-equivalent host
metadata. A future L2 allocator should avoid full free-list scans and preserve
larger free extents/buckets rather than searching linearly per allocation.
### C126 — 2026-06-02 L2 HostKVCache allocator uses lazy page extents instead of full free-slot scans
Finding:
- `HostKVCache.alloc_contiguous_preferred()` previously scanned/materialized the
full token-level `free_slots` tensor to discover contiguous physical page runs.
On 220GB-equivalent host metadata this could cost hundreds of microseconds to
milliseconds per reservation.
- The full scan is the wrong shape for CP HiCache: allocations and releases are
page-shaped, and the fast path only needs page-run metadata plus a token-index
tensor for the chosen pages.
Correction:
- `HostKVCache` now maintains a lazy page-extent index:
- `free_slots` remains a compatibility property and is materialized lazily only
when external code reads it.
- `available_size()` reads an integer token count and does not materialize.
- `free()` converts page-shaped token indices into page runs and merges them
into sorted extents.
- `alloc_contiguous_preferred()` first checks the largest free extent for a
single-run allocation; if no run is large enough, it falls back to a batched
fragmented run allocation without scanning/sorting the whole `free_slots`
tensor.
- The fallback is still page-shaped and returns normal token indices. It avoids
silent corruption: overlapping/double-free page extents raise, and non-page
shaped frees disable the extent index with a warning before falling back to the
legacy tensor path.
C126 validation update:
- Remote RED/GREEN target: the new lazy extent-index unit first failed on missing
`_free_slots_dirty`, then passed after implementation.
- Remote `test_hicache_controller_cp.py`: `67 passed`.
- Remote `test_cp_hicache_allocator_bench.py`: `10 passed`.
- Remote 220GB-equivalent production `HostKVCache` churn sample with `burnin=20`,
`occ=0.97`, `evict_pages=512` after optimization:
- `request_pages=64`, `prefill_node_pages=64`: contiguous p50 ~31 us.
- `request_pages=512`, `prefill_node_pages=64`: contiguous p50 ~86 us.
- `request_pages=512`, `prefill_node_pages=1`: contiguous p50 ~292 us; this
is no longer a full free-list scan, but mostly fragmented selection plus
constructing a 32768-token host-index tensor.
- Compared with the previous C125 sample, the contiguous path drops from roughly
0.52.6 ms p50 to roughly 30292 us p50 on this benchmark shape.
Remaining risk:
- `alloc()` now uses the extent-backed fragmented allocation when the extent
index is active, so exact FIFO order is no longer the internal contract. This
should be acceptable for host KV slots because callers require unique free
page-shaped slots, not FIFO identity, but future code must not depend on
token-level FIFO order.
- Very fragmented tiny historical nodes still produce poor LPF coalescing. That
requires higher-level eviction/allocation policy to preserve larger free runs;
the allocator now avoids the worst CPU scan cost but cannot create physical
contiguity that the free set does not contain.