Repository navigation
Object id PR 1a: no home PE in the id, 49-bit packed index, hashed ids with per-process tranches, delivery by id - #4017
Conversation
84ea45a to
19d05e5
Compare
…ds with a per-process tranche; delivery by id PR 1a of the object id redesign (#3994; design in doc/objid64-design.md on this branch, sections 1-6). The layout, the home function and the delivery path change here; the tranche allocator with refills and the per-process directory follow in later changes. Layout (objid.h). The id is 3 type tag bits, CMK_OBJID_COLLECTION_BITS (default 12, build-time) and a 49-bit payload. CMK_OBJID_HOME_BITS is gone: no PE number is stored in the id. An array with bounds that fit packs its index into the whole payload (16 bits before). An array without a usable compressor gets a "hashed" payload: a hash key of the index in the top bits and a number unique within the array in the low bits. The key width is fixed per run from the process count and the +objid_expand factor (default 8: the presumed ceiling on later expansion), stored in ck::objid::Layout, one copy per process, and written into every checkpoint with the readonlies so a restart keeps the layout the ids were minted under. One home per element (cklocation.h). CkLocMgr::homePe(idx) and homePe(id) now agree by construction: a packed-index array uses its map, as before, decoding the index from the id when needed; a hashed array hashes the index to a process (key % CkNumNodes(), rank 0 of it) from the id or from the index alike. The old CkLocMgr::homePe(id), which read home bits, and the creator-PE home of counter-minted ids are gone, and with them the two homes that disagreed for every dynamically inserted array. CkLocCache gets its manager back-pointer so it can compute the home of an id. Minting (cklocation.C). A hashed id is minted synchronously on the inserting PE from its process's tranche of unique numbers: the lower half of the unique space split evenly among the processes present at launch, with one atomic cursor per process per array. Exhausting the initial tranche aborts with a message; refills come with the allocator in a later change. The per-PE idCounter and its silent overflow are gone. The index hash is a splitmix64 mix of the index words, not CkArrayIndex::hash(). Delivery (ckarray.C). A receive-side cache miss forwards to homePe(id) through handleUnknownByID (Aditya Bhosale's e417584, folded in): the index is never recovered off the source PE, so lookupIdx loses its linear scan of idx2id and aborts if asked about an element this PE has never seen. A sender that forwards via the home now also asks for the location once (requestLocationOnce), so later sends do not repeat the detour. The disabled "after two hops, send home" rule is enabled: sound now that the home of an id is the home of its index. Load balancer global update. Under CMK_GLOBAL_LOCATION_UPDATE the emigrating PE broadcasts the new location with the element's true epoch, in place of UpdateLocation fabricating one from each receiver's stale cache (which let an older reply overwrite a newer location). Restart. Ids never change: packed ids are functions of the index, hashed ids are restored verbatim. Directories are rebuilt, not restored: restore() now informs the home like resume() does, and the __FAULT__ branch of CkLocCache::pup that shipped the home's remote entries is dropped. The process's tranche cursor travels with rank 0's branch. Until the allocator lands, restarting with a different number of processes aborts for hashed arrays, with a message naming the array; packed arrays restart on any process and PE count, and hashed arrays on any PE count. New test tests/charm++/unbounded_restart: checkpoint an unbounded array, restart on the same and on half the PEs, ping every element. Tested on reconverse-darwin-arm8 (production), single process and with 2-3 processes: pingpong, megatest, hello/4darray, startupTest, anytime_bcastred, unbounded_migration (also on 6 PEs across 3 processes), the three load_balancing tests with RotateLB, chkpt (same count, and 4->2 and 2->4 PEs in one process), unbounded_restart, and a 200,000-element unbounded insert from one PE that the counter scheme aborted at 65,536. Not tested: a 2-process x 2-PE restart, which hangs on the unmodified branch as well. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
recvMsg's "after two hops go home" rule replaced the cached PE with the home PE whenever the cached PE was not this PE. At the home itself that made the home re-send the message to itself forever, so an element that had migrated twice never received it (anytime_bcastred hung at step 3 in the reconverse stress job, seed 1; every seed on the Mac). Apply the rule only on PEs other than the home; the home follows its own entry. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
bufferForCreation buffered the message and sent requestDemandCreation to the home, but for createhome the request named the home itself as the creation PE, so after creating the element the home told nobody. The requester's bufferedCreationMsgs entry was never flushed: only messages sent from the home PE ever reached a demand-created element, on the pre-redesign runtime too. The packed kind hid it because the source holds the id and forwards the small message to the home instead of buffering. Pair the request with a location request by index (new CkLocMgr::requestLocationAtHome); the home buffers it until the element exists and its reply flushes the buffered messages. Found by the new tests/charm++/objid_insert test on the hashed kind. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Packed 3D array with 64^3 bounds (18 index bits, beyond the old 16-bit budget) and a hashed 2D array. From every PE, for every element: homePe(idx) == homePe(id); packed ids are computable on PEs that never saw the index; hashed ids satisfy key == indexHashKey(idx) and home == rank 0 of key % CkNumNodes(); all PEs agree on every home; ids are distinct and each hashed unique part lies in its creating process's initial tranche. Repeated after two migrations of every element: ids and homes unchanged. The tranche-size check recomputes the file-local formula of cklocation.C (initial region halved and split by process count); update it together. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…d creation, deletion Runs in both kinds (-b for packed). Every PE inserts elements locally (and checks ckLocal() is non-null on return) and on the next PE; every PE then sends by index to every element, including ones it never touched; elements migrate to random PEs and the sends repeat over one-hop and two-hop stale caches (the recvMsg "more than one hop, go home" rule), five cycles; a [createhome] entry sent from every PE to never-inserted indices must create each element exactly once at its home; half the elements destroy themselves and the sends repeat over the survivors. Uses thisProxy[thisIndex].ckDestroy(): a direct ckDestroy() inside a broadcast entry method segfaults in CkArrayBroadcaster::attemptDelivery on the base runtime as well (to be filed separately). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…array With -u the array is created without bounds and inserted dynamically, so its element ids are the hashed kind (home = rank 0 of the process chosen by the index hash) instead of the packed index. Behaviour without -u is unchanged. The test targets and the reconverse stress job run both. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nder migration Two overlapping sections of a 32-element unbounded array are built once; for 30 rounds main multicasts to both, each member checks it was reached through a section it belongs to and that its id is unchanged since insertion, section reductions are checked, and then every element migrates to a random PE. Section cookies and spanning trees hold element ids, which the redesign keeps stable for the element's lifetime. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ale null pointer deliverInline's bound-sibling branch demand-creates the missing element and then delivered to the pointer looked up before the creation, which is still null; CkAssert(elem) in deliverToElement is compiled out in production, so the first message to a demand-created bound sibling dereferenced null (also on upstream main and classic netlrts). Re-look the element up after creation and abort with a message if it is still absent. Found by tests/charm++/objid_bound. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…anual and LB migration A is unbounded (hashed ids); B is bound to A and inserted; C is bound to A, never inserted, and reached only through a [createhome] entry, so its elements are demand-created on the sibling's PE by deliverInline's bound-sibling branch (the one remaining user of the reverse lookup). Each round A[i] messages B[i] and C[i], which check they are co-located with A[i], and B[i] messages A[i+1]; then A migrates (migrateMe, or AtSync with +balancer RotateLB / GreedyLB) and B and C must follow: their migration counts are enforced equal to A's. Runs single-process and under lcrun. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
19d05e5 to
dca0730
Compare
|
Review of this PR at dca0730 against Must fix1. Demand creation is lost when the message reaches the home by id (regression for bounded arrays).
2.
3.
Smaller
Looks right
|
Brings in #4016 (PR 0, squash-merged there; this branch had the original commits), #4014 CkTreeCacheManager, #4020 trace-summary message bytes, and the 2026-09-30 triage documents. The three conflicts were PR 0 text that PR 1a (#4017) had already rewritten; the objid-redesign side is kept in each, so the 1a files are unchanged by this merge. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
This merged on the 2026-10-01 approval, ahead of Aditya's 2026-10-04 review above. The merged head (dca0730) is exactly the commit that review examined, so its three must-fix items are now in
A fix PR against |
…, synchronous LB location update Addresses Aditya's review of #4017 (2026-10-04), which was merged into objid-redesign before the review was acted on. The three must-fix items and the smaller ones; design doc sections 3.4 and 4.4 record what was done. 1. Demand creation is lost when a message reaches the home by id. CkArray::handleUnknownByID parked every message in bufferedIDMsgs and asked for a location that nothing would ever report, because the element did not exist. For a message that asks for creation it now recovers the index and takes the index-keyed path (handleUnknown), which approves creation through the home for createhome and creates at the original sender for createhere. New CkLocMgr::recoverIndex(id, idx, scanAtHome): the compressor, a local record, or, for a hashed-kind element at its home, a linear scan of the home's idx -> id bindings (kept by reclaimRemote; the case is the re-creation of a deleted element, and no delivery path uses the scan). A non-home PE that cannot name the index hands the message to the home. 2. requestLocation(idx, peToTell) answered with the null entry. For a packed-index array the id of any in-bounds index is always known, so the home replied as soon as it was asked, with pe -1 and id 0, which asserts in updateLocation (debug builds) and in production sends buffered messages for id 0 to PE -1. The home now answers only when it knows the location and otherwise holds the request in bufferedLocationRequests, which updateLocation answers once the element registers. 3. CMK_GLOBAL_LOCATION_UPDATE: the update was no longer synchronous. UpdateLocation had been emptied in favour of a broadcast from the emigrating PE, so a PE could resume after a balancing step and address an element at its old location (an extra hop; GPU zerocopy aborts on it, and multiHop asserts in this mode). The synchronous update is back, keyed by id (CkLocMgr::updateLocationFromLB), with the epoch counted from the receiver's cache as before; that count never exceeds the element's true epoch, so the destination's insert still applies over it. The broadcast stays, carrying the true epoch, which closes the stale-reply window of the counted epoch; cost one group broadcast per migration, in this mode only. Carrying the epoch in MigrateInfo would remove it (later). Smaller: decompress masks with a 64-bit shift (a dimension may need 31 bits now); pupLayout writes a marker so a checkpoint without a layout record fails clearly instead of misreading the readonly messages; the hashed-kind restart abort distinguishes a changed process count from a changed PE-per-process layout; the uniqueBits < 16 check moves from initLayout to the first hashed array's getTranche, so a run with only packed arrays never trips it. Tests (tests/charm++/objid_insert): -n makes only non-home PEs send the createhome demand creations, so the creation must come from a message that reaches the home by id (fails without fix 1 on the packed kind: the elements are never created, watchdog abort). Phase 4b adds createhere demand creation with a single sender per element (fails without fix 2 on the packed kind in a debug build: the null-entry assert). Phase 6 re-creates the elements destroyed in phase 5 by createhome, from senders holding stale ids and locations, which is the only path to the home-side index scan of the hashed kind. Re-creation by createhere is documented as not tested: the creating PE's insertElement follows its own stale location entry (the isRemote redirect for bound siblings) and a re-created element restarts at epoch 0, both of which predate the redesign. The Makefile runs both kinds with and without -n. lb_test under a -DCMK_GLOBAL_LOCATION_UPDATE=1 debug build with GreedyLB and RotateLB passes with the fix; it also passed without it on this laptop (single process, 4 PEs), where the emigrate broadcast lands before the resume. The window is real on a network, since nothing orders the two; fix 3 closes it by construction (every PE is updated before the move is acted on) rather than by a reproduced failure. Not changed: pendingLocReqs entries can stay set when the home drops a request for an id it does not know; PR 1b removes requestLocationOnce. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The explicit location requests went away in "a forwarded delivery repairs the sender's cache"; the helper and its pending set had no callers left. This closes the last item of Aditya's review of #4017 (an entry in pendingLocReqs stayed set when the home dropped a request), which #4028 left to this PR. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
PR 1a of the object id redesign (#3994), stacked on
objid-redesign; design indoc/objid64-design.mdsections 1-6 on that branch. Merges into the integration branch, not the reviewed line.What changes
CMK_OBJID_COLLECTION_BITS) | payload 49.CMK_OBJID_HOME_BITSis gone; no PE number lives in the id. Bounded arrays pack the index into the whole payload (16 bits before). Unbounded arrays get a hash key of the index plus a number unique within the array; the key width is set per run from the process count and+objid_expand(default 8) and travels with every checkpoint.homePe(idx)andhomePe(id)agree by construction (map for packed arrays; index hash to a process for hashed arrays). The cache gets its manager back-pointer to compute it.homePe(id)viahandleUnknownByID(Aditya's e417584 folded in);lookupIdxloses its linear scan;requestLocationOncestops repeated detours; the "two hops then home" rule is enabled.__FAULT__cache pup is dropped). Restarting with a different number of processes aborts for hashed arrays until PR 1b; packed arrays restart on any count.tests/charm++/unbounded_restart.Tested on reconverse-darwin-arm8 (production), 1-3 processes: pingpong, megatest, hello/4darray, startupTest, anytime_bcastred, unbounded_migration, the three load_balancing tests with RotateLB, chkpt (same count; 4->2 and 2->4 PEs), unbounded_restart, and a 200,000-element unbounded insert from one PE. Found on the way, not caused here (reproduced on the unmodified build): a 2-process x 2-PE checkpoint restart hangs after "CkRestartMain done"; TreeLB asserts when a program calls migrateMe under an active balancer (program error).
Confidence tests and fixes added 2026-09-30
Five test programs, each run single-process and under
lcrunwith 2 and 4 processes on macOS (in a single process every hashed-kind home is PE 0, so the multi-process runs are the ones that exercise hashed-kind homes):tests/charm++/objid_home: from every PE,homePe(idx) == homePe(id), packed ids computable on PEs that never saw the index, hashed ids obey the key/unique split and land in the creating process's tranche, ids and homes unchanged after two migrations.tests/charm++/objid_insert(both kinds): insertion from every PE, synchronous[inline]local insertion, cold sends by index, sends over one- and two-hop-stale caches,[createhome]demand creation,ckDestroy.tests/charm++/anytime_bcastred -u: the existing migration stress on an unbounded array; the reconverse stress job runs it too.tests/charm++/objid_sections: ckmulticast sections on a hashed-kind array, migrating every round.tests/charm++/objid_bound: bound arrays over a hashed-kind array undermigrateMeand under+balancer RotateLB/GreedyLB, including a never-inserted bound array reached only through[createhome](deliverInline's bound-sibling demand creation, the last user of the reverse lookup).Fixes they found, each its own commit:
recvMsgredirected to the home on the home PE itself, so the home re-sent the message to itself forever (this PR's regression; failedanytime_bcastredin CI).[createhome]request from a PE with no id never got a reply, so the requester's buffered messages were never delivered.bufferForCreationnow also requests the location by index.Not fixed, to file separately: a direct
ckDestroy()inside a broadcast entry method segfaults inCkArrayBroadcaster::attemptDelivery(base runtime too); the tests usethisProxy[thisIndex].ckDestroy().🤖 Generated with Claude Code