Repository navigation
Conversation
YAMY1234
left a comment
There was a problem hiding this comment.
Thanks for putting this together. Rotating the owner per layer is a real improvement over the rank-0-only dedup: it spreads D2H/H2D traffic across ranks and still gives ~tp_size x unique host capacity. A few things I'd like to see addressed before this moves out of draft (inline comments below).
Tests. The multi-process simulations from the description aren't in the PR. Could you add unit cases for owner rotation, shard layer mapping and host capacity sizing to the existing test_mla_host_dedup_primitives.py, plus the end-to-end accuracy run (GSM8K with forced eviction/load-back) on the combined branch?
| def begin_load(self, transfers: list, h2d_stream) -> RankShardLoad: | ||
| return RankShardLoad(self, transfers, h2d_stream) | ||
|
|
||
| def exchange(self, rows: torch.Tensor, page_index: torch.Tensor, owner: int): |
There was a problem hiding this comment.
This is nearly line-for-line the same as MLAHostDedupBroadcaster._bcast_layer (index_select -> broadcast -> index_copy_), and that primitive on main already has two users (HiCache dedup and the Mooncake linker MLA dedup). Could you add a per-layer src to _bcast_layer / broadcast_loaded_layer and build rotating ownership on top of it, instead of a separate communicator and lifecycle? That would also pick up its DP-attention support (attn_tp_group), which this PR currently excludes.
| ) | ||
| mark = device_module.Event(enable_timing=True) | ||
| mark.record(stream) | ||
| stream.wait_event(self.done_event) |
There was a problem hiding this comment.
done_event is recorded after the last layer's exchange, so whenever a sharded load-back is pending the next forward starts only after all layers are loaded and broadcast; this removes HiCache's layer-wise load/compute overlap. Could you share profiles (TTFT and load-back time: sharding off vs. rank-0 dedup vs. this PR) showing the 1/tp H2D savings outweigh the lost overlap? The Mooncake linker dedup on main issues its broadcasts on the forward stream, which keeps ordering with the forward's collectives without a gate; worth checking whether that works here too.
| f"HiCache rank shard needs page-aligned device indices, got " | ||
| f"{di.numel()} slots for page_size={page}" | ||
| ) | ||
| pages = di.cpu()[::page] // page |
There was a problem hiding this comment.
If device_indices is on the GPU, di.cpu() blocks the scheduler thread on every load-back, and the result is then copied back to the device. MLAHostDedupBroadcaster.prepare_broadcast computes the same page index on device; could this do the same?
| # up and loads back only its round-robin share of the host layers; loaded | ||
| # layers are broadcast from their owner over a dedicated NCCL group. Host | ||
| # capacity in tokens grows ~tp_size x at the same --hicache-size. Off by default. | ||
| SGLANG_ENABLE_HICACHE_RANK_SHARD = EnvBool(False) |
There was a problem hiding this comment.
Rank shard alone adds five env vars, plus _Stats, _GateStats, a gloo verify group and the events timer, roughly half of the new file. I'd keep only the enable flag (and maybe staging size) upstream, and leave the debug/telemetry pieces in a local branch.
| cache_controller.load_fence_stream = ( | ||
| self.tp_worker.model_runner.forward_stream | ||
| ) | ||
| enable_gate = getattr( |
There was a problem hiding this comment.
Per .claude/rules/no-getattr-defensive.md, please avoid getattr here; enable_rank_shard_forward_gate always exists on the controller, so call it directly. Similarly, the module-level mutable _d2h_issue_chunks_logged and the dict-based _events_timer would be cleaner as instance state or a small struct.
SGLANG_HICACHE_D2H_ISSUE_CHUNK_PAGES=n splits each page_first_direct all-layer backup batch (MLA KV and DSA indexer) into n-page batches on the same stream, in page order, so each batch's copy runs while the next is issued. Same copies, same bytes, same stream order. Unset or 0: one call, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
check_hicache_events runs a CPU (gloo) MIN all-reduce of the D->H and H->D ready-ack counts on every scheduler step. While both ack queues are empty on every rank the reduced counts are zero, so the step changes no state. SGLANG_ENABLE_HICACHE_IDLE_SYNC_SKIP=1 skips that all-reduce while no ack is outstanding and no storage backend is attached. Every input to the decision (queue lengths, enable_storage, the skip counter) is replicated across ranks, so all ranks skip or sync together; the decision is wrapped in rank_consensus. One sync is forced every 512 consecutive skips, so the write_back duplicate-reclaim digest check piggybacked on it still runs. The skip is limited to aggregated serving with one PP stage and no buffer-only host mode; otherwise it logs why it is inactive. Default off; unset, the code path is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oject#36800 primitives Every attention-TP rank holds the same MLA latent KV (and DSA index keys), so HiCache backs up, stores and loads back the same bytes on every rank. --enable-mla-hicache-host-dedup wires the MLA host-dedup primitives from sgl-project#36800 into the HiCache L2 tier, with each layer's host copy owned by one rank in turn rather than all by one source rank: - MLAHostDedupLayerOwners: KV layer i belongs to rank i % size; DSA index-key layers continue the rotation after the KV layers. MLAHostDedupBroadcaster takes optional owners: _bcast_layer takes each layer's source from its owner and the warm-up runs from every source rank. Without owners it keeps its single-source behavior. Shared-top-k index layers (0-row placeholders) hold no index keys and are skipped. The index-key staging is sized in pages, its row unit, so it covers the same tokens as the KV staging; it was sized as if each page row were a token. - Backup (D2H): a rank copies only the layers it owns into a host buffer that holds only those layers; packed MTP draft layers stay on every rank. Token capacity is sized by the largest share, so every rank's host pool holds the same tokens and the radix tree and slot allocation stay replicated. The MLA host pool reuses its layer-ownership hooks; the DSA indexer host pool keeps only the index layers it owns. - Load-back (H2D): a rank loads only its own layers. The forward broadcasts each layer from its owner through the HiCache layer-wait hook (LayerDoneCounter.on_layer_ready), on the forward stream right after its wait for that layer's H2D copy, as the Mooncake linker dedup does. Each layer is broadcast once, in layer order, at the same point of the forward on every rank, so no gate or side stream is needed, and later layers keep loading while earlier ones compute. The load's ack event is recorded again after the last broadcast, so the ack and the allocator's transfer fence cover the broadcasts. - DP attention: the owners and the broadcast group follow the attention-TP group, as the primitives already do. Active with more than one attention-TP rank, no attention CP or DCP, one PP stage, one node, direct I/O with page_first_direct, no storage backend (a runtime attach is rejected), no hisparse, not PD decode, a non-FP4 MLA/DSA pool without device layer sharding, and at least as many KV (and index-key) layers as ranks; otherwise it logs why it is inactive. Off by default: no owners, context or group is created, and every path is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
b9e28fd to
7e939a8
Compare
Motivation
Three costs show up in long-context, cache-heavy HiCache serving with MLA/DSA models under TP:
page_first_direct, each backup is issued as one large batch of per-page copies; the host issues all copy descriptors before the first copy starts, so issue time and transfer time add up.check_hicache_eventsruns a CPU all-reduce of the ready-ack counts on every scheduler step, even when no transfer is pending on any rank.Modifications
Three commits, each off by default (with all of them unset, the code path is unchanged):
1. Chunked D2H backup issue.
SGLANG_HICACHE_D2H_ISSUE_CHUNK_PAGES=nsplits each all-layer backup batch (MLA KV and DSA indexer) into n-page batches on the same stream, in page order, so each batch's copy runs while the next one is issued. Same copies, same bytes, same stream order.2. Idle-sync skip.
SGLANG_ENABLE_HICACHE_IDLE_SYNC_SKIP=1skips the ready-count all-reduce while no ack is outstanding and no storage backend is attached. All inputs to the decision are replicated across ranks (wrapped inrank_consensus), so all ranks skip or sync together; one sync is still forced after a fixed number of skips so the write_back duplicate-reclaim digest check keeps running.SGLANG_HICACHE_EVENTS_TIMER_STEPS=Noptionally logs the time spent incheck_hicache_eventsevery N calls.3. Rank-sharded host tier.
SGLANG_ENABLE_HICACHE_RANK_SHARD=1gives each host layer of the KV and indexer host pools one owner rank (round-robin over the TP ranks):tp_sizeat the same--hicache-size(SGLANG_HICACHE_RANK_SHARD_KEEP_CAPACITY=1keeps the old capacity and uses less host memory instead).start_loading(behind the load fence; anything else raises), and before the next forward's first kernel (Scheduler.run_batchandLayerDoneCounter.set_consumer) the forward stream waits for every submitted exchange to finish. Without this gate the exchange can deadlock against the forward's collectives.page_first_direct, no storage backend, not PD decode, and a non-FP4 MLA pool; otherwise it logs why it is inactive. Extra knobs:SGLANG_HICACHE_RANK_SHARD_STAGING_MB,SGLANG_HICACHE_RANK_SHARD_LOG_EVERY, andSGLANG_HICACHE_RANK_SHARD_VERIFY_EVERY(periodic cross-rank check that all ranks loaded the same host pages).L2TransferEngine._load_layer.When both 1 and 3 are enabled, the chunked issue runs over the owner-only layers.
This PR carries only the synchronous load-back path. If the async load-back issue work (#38472) lands, rank sharding needs a few small hooks there, which I can add.
Accuracy Tests
Speed Tests and Profiling
Rank sharding cuts each rank's host copy volume to about
1/tp_sizeand multiplies unique host capacity by abouttp_size, so far fewer requests fall out of the host tier and re-prefill; chunked issue overlaps copy issue with transfer; the idle-sync skip removes a per-step CPU collective whenever HiCache is idle. The gains are largest where HiCache load-back and write-back dominate. Happy to share detailed profiles with reviewers.Related: #26691 (host dedup; closed), #36800, #39565, #39903 (pipelines the same all-reduce; closed), #30511, #37562.
Checklist
🤖 Generated with Claude Code
CI States
Latest PR Test (Base): ❌ Run #37351639014
Latest PR Test (Extra): ❌ Run #37351638781
Latest PR Test (AMD ROCm 10): ❌ Run #37351639104