Repository navigation
[Perf] disaggregation: Cache host-staged decode admission accounting across a transfer pass - #48037
Merged
Merged
Conversation
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
metamergebot
requested review from
Duyi-Wang,
HaiShaw,
ShangmingCai and
sogalin
as code owners
November 3, 2026 02:24
…across a transfer pass A decode transfer pass admits host-staged requests to device memory in queue order. Since the pass stopped at the first refusal, a later, smaller request that would fit had to wait for the next pass even when device capacity was available. Letting every request in the pass try, with the previous per-call accounting, would rescan the running batch, the transfer queue and the retracted queue on each attempt: O(W * (R + Q + T)) per pass for W attempts, R running requests, Q transfer entries and T retracted requests. Cache the parts of the admission accounting that do not change inside one pass: the running-batch token sums, the maximum per-request growth, the retracted-token requirements and the count of transfer entries holding a device destination. The cache is scoped to the admission calls of a single pass with a context manager, so every other caller, the next pass, and the path after an exception read the live queues. A successful admission increments the cached device-destination count; refusals reuse it, bringing the pass to O(R + Q + T + W). Allocator availability, eviction credit and SWA capacity stay live so allocation, reclamation and failed-transfer cleanup affect the next decision. The unified-memory transfer pin predicate reads the same device-destination count through a new `has_device_destinations` property instead of scanning the transfer queue on every call, and the move-gate test's fake prealloc queue now inherits that property from `DecodePreallocQueue`. The cache holds Python integers only; the admission path performs no device readback or GPU-to-CPU synchronization. Co-authored-by: ZYHowell <ZYHowell@users.noreply.github.com>
metamergebot
force-pushed
the
oss-sync-621ba2d1a4
branch
from
November 3, 2026 10:32
bc19c04 to
dcf1c92
Compare
Collaborator
Author
|
This port clears our bar on gate✓ lint✓ build 3✓✗2✂1 A✓ B✓ C✓ other✓ · accel ✗amd @charlotte12l @merrymercy @cctry @ZYHowell — could one of you take a look and merge it if you are happy? Nothing else is waiting on our side. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
DecodeTransferQueue.pop_transferredadmits completed host-staged requests to device memory throughDecodePreallocQueue.allocate_host_staged. #46898 made a pass stop device admission at the first refusal, because trying every later request recomputed the request-independent accounting (running-batch token sums, maximum per-request growth, transfer-queue device-destination count, retracted-token requirements) on every attempt: O(W × (R + Q + T)) per pass for W attempts, R running requests, Q transfer entries and T retracted requests.Stopping at the first refusal trades utilization for that bound. A later, smaller request that would fit waits for the next pass even when device capacity is free, and near a full pool that is exactly when the capacity matters. This change lets every completed request in the pass try, and keeps the pass linear by computing the shared accounting once.
Modifications
_HostStagedAdmissionStatsholds the pass-invariant accounting: retractable full/SWA token sums over the running batch, the maximum per-request growth (full and SWA), the number of transfer entries holding a device destination, and the retracted queue's full/SWA token requirements. Each field is filled lazily on first use and reused by later admission calls in the same pass.DecodePreallocQueue._use_host_staged_admission_statsscopes the cache with a context manager around eachallocate_host_stagedcall inpop_transferred. Outside that scope (every other caller, the next pass, and after an exception)_host_staged_admission_statsisNoneand all helpers read the live queues as before.continueand later ones still try, in queue order._need_space_for_single_reqreturns early for an empty running batch or aNoneretractable count, then computes the maximum per-request requirement once and subtracts the retractable tokens; the result is unchanged._retracted_token_countsreturns the retracted queue's(full, swa)requirements in one walk and replaces the two separate loops in the full and SWA allocatable-budget helpers.has_device_destinationsproperty onDecodePreallocQueue;disagg_transfers_in_flightindisaggregation/utils.pyreads it instead of scanning the transfer queue itself, so the unified-memory pin predicate also benefits from the cached count during admission.test_unified_memory_move_gate.py:_FakePreallocQueuenow subclassesDecodePreallocQueueand holds the fake transfer queue, so the move-gate test exercises the realhas_device_destinationsproperty.Rebased onto main after #43908: the two new iterations over the decode transfer queue (
has_device_destinationsand_active_req_count) readself.transfer_queue.queue.items(), matching the request-list container that refactor introduced; thepop_transferredloop keeps itsqueue = list(self.queue.items())form.Allocator availability, eviction credit and SWA capacity are not cached; allocation, reclamation and failed-transfer cleanup inside a pass affect the next decision. The cache holds Python integers only; the admission path adds no device readback or GPU-to-CPU synchronization.
Testing
pre-commit run --fileson the three changed files passes.test/registered/unit/disaggregation/test_unified_memory_move_gate.py: 10 tests, 22 subtests pass on this branch.test/registered/unit/mem_cache/test_decode_radix_lock_ref.py,test_decode_retraction_backup.py,test_hisparse_allocator.pyandtest_unified_dynamic_gate_capacity.py(the registered unit files that exercise the changed admission helpers): 35 tests, 23 subtests pass on this branch.Co-authored-by: ZYHowell ZYHowell@users.noreply.github.com
CI States
Latest PR Test (Base): ✅ Run #41620158335
Latest PR Test (Extra): ❌ Run #41620158058
Latest PR Test (AMD ROCm 10): ❌ Run #41620158319
Tip
Base CI only covers the core cases, and its workload may not exercise your change. Use
/rerun-testto run the tests your change touches, or copy this prompt for an agent: