From 6cbc3b7738322411ee9d8d4aa20fc326a46b6150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sat, 5 Sep 2026 21:27:42 +0200 Subject: [PATCH 01/11] perf(gc): route the trigger path and dirty-page barrier through hot TLS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `gc_check_trigger` runs on every `gc_malloc`, and `gc_budgeted_due_trigger` resolved eleven raw `thread_local!` declarations one `_tlv_get_addr` call at a time. Measured with `sample` on the compiled claude-code TUI streaming a 3300-char reply (14,578 active main-thread samples, callers resolved by an explicit ancestor walk): `_tlv_get_addr` was 380 main-thread leaf samples, 71 of them with `gc_budgeted_due_trigger` as the immediate caller, 36 in `old_page_account_dirty_slots`, 31 in `scan_dirty_object_slots`, 27 in `gc_malloc_header_is_tracked`. Sixty-seven declarations move to `crate::perry_thread_local!`. Why they were still cold is a measurement bug in the gate, not an oversight: `scripts/check_thread_locals.py` ratchets on raw `thread_local!` BLOCKS per file, and a block holds any number of declarations — so `gc/policy.rs` counted as 6 while declaring 28, and adding a `static` to a recorded block passed silently. In the same unit as the hot side, main was 318 hot against 339 cold declarations. The gate now ratchets on declarations (385/272) and `--self-test` gained the direction that catches it. `ARENA_TOTAL_BYTES`, `BLOCK_POOL` and `BLOCK_POOL_BYTES` stay raw and say so: they are read from `Arena::new`, which runs as `tls_hot::fill`'s first provider, so a `HotKey` there re-enters `fill` — which has not yet written the `temp_roots` field it gates on — and re-runs `ARENA`'s initializer without bound. Claude-Session: https://claude.ai/code/session_014UZWia6L37DpA93VLtNK9m --- changelog.d/9830-gc-trigger-path-hot-tls.md | 55 +++++++++++ crates/perry-runtime/src/arena/block.rs | 44 +++++++++ crates/perry-runtime/src/arena/mod.rs | 2 + crates/perry-runtime/src/arena/page_meta.rs | 9 +- crates/perry-runtime/src/gc/barrier/mod.rs | 8 +- crates/perry-runtime/src/gc/malloc.rs | 17 +++- crates/perry-runtime/src/gc/old_free.rs | 10 +- crates/perry-runtime/src/gc/policy.rs | 62 ++++++++++-- crates/perry-runtime/src/gc/tenuring.rs | 6 +- crates/perry-runtime/src/gc/tests/mod.rs | 2 + .../src/gc/tests/tls_fill_reentrancy.rs | 43 +++++++++ .../src/gc/tests/trigger_path_tls.rs | 67 +++++++++++++ crates/perry-runtime/src/gc/trace.rs | 6 +- scripts/check_thread_locals.py | 62 +++++++++--- scripts/gc_runtime_root_holders.json | 2 +- scripts/thread_local_cold_allowlist.json | 94 +++++++++---------- 16 files changed, 407 insertions(+), 82 deletions(-) create mode 100644 changelog.d/9830-gc-trigger-path-hot-tls.md create mode 100644 crates/perry-runtime/src/gc/tests/tls_fill_reentrancy.rs create mode 100644 crates/perry-runtime/src/gc/tests/trigger_path_tls.rs diff --git a/changelog.d/9830-gc-trigger-path-hot-tls.md b/changelog.d/9830-gc-trigger-path-hot-tls.md new file mode 100644 index 0000000000..e791ac3b7d --- /dev/null +++ b/changelog.d/9830-gc-trigger-path-hot-tls.md @@ -0,0 +1,55 @@ +**The GC trigger path and the dirty-page barrier stop paying `_tlv_get_addr` +per read, and the policy gate that let them stop paying it now counts the +thing it is bounding.** + +`gc_check_trigger` runs on every `gc_malloc`, and its predicate +(`gc_budgeted_due_trigger`) resolved eleven raw `thread_local!` declarations +one out-of-line call at a time. Measured with `sample` on the compiled +claude-code TUI streaming a 3300-char reply (14,578 active main-thread +samples, callers resolved by an explicit ancestor walk rather than +nearest-symbol labels): `_tlv_get_addr` was 380 main-thread leaf samples, +**71 of them with `gc_budgeted_due_trigger` as the immediate caller**, 36 in +`old_page_account_dirty_slots`, 31 in `scan_dirty_object_slots`, 27 in +`gc_malloc_header_is_tracked`. `crates/perry-runtime/src/tls_hot.rs` has +existed to abolish exactly this since #7469; the allocation path's *fields* +were covered and the trigger path never was. + +Sixty-seven declarations across `gc/policy.rs`, `gc/malloc.rs`, `gc/old_free.rs`, +`gc/tenuring.rs`, `gc/trace.rs`, `gc/barrier/mod.rs`, `arena/block.rs` and +`arena/page_meta.rs` move to `crate::perry_thread_local!` — same syntax, same +`.with()` at every call site, the address served from this thread's hot cache +instead of a libdyld call. + +**Why they were still cold is a measurement bug in the gate, not an oversight +anyone could have noticed.** `scripts/check_thread_locals.py` ratchets on the +number of raw `thread_local!` **blocks** per file, while `thread_local! { … }` +holds any number of declarations — so `gc/policy.rs` counted as **6** while +declaring **28**, and adding a `static` to an already-recorded block passed +the gate silently. Counted in the same unit as the hot side, `main` was **318 +hot declarations against 339 cold ones** — cold was the majority, reported as +a 2.6:1 minority. The gate now ratchets on declarations (`385 hot / 272 +cold`), and `--self-test` grew a seventh direction that fails when a `static` +is added to a recorded block; restoring the block count makes that case, and +only that case, fail. + +Three declarations stay deliberately raw and say so at their declaration: +`ARENA_TOTAL_BYTES`, `BLOCK_POOL` and `BLOCK_POOL_BYTES` are read from +`Arena::new`, which runs as `tls_hot::fill`'s **first** provider, so a +`HotKey` there re-enters `fill` — which by design has not yet written the +`temp_roots` field it gates on — and re-runs `ARENA`'s initializer without +bound. It is a stack overflow at thread start, not a slow path, and it is the +first documented instance of the rule that a declaration read from inside a +`fill` provider cannot use the macro. `gc::tests::tls_fill_reentrancy` is the +standing guard, and it is sabotage-proved: moving `ARENA_TOTAL_BYTES` alone +into the neighbouring hot block aborts that test with `fatal runtime error: +stack overflow`. + +`gc::tests::trigger_path_tls` is the runtime half of the gate: it drives +`gc_check_trigger` on a fresh thread and asserts every trigger-path +declaration owns a hot slot and that the path publishes slots at all. +Reverting any one of them to a raw `thread_local!` removes `slot_index` and +breaks the build at that declaration's own name. It is a test that can fail +and did: the first run rejected `GC_DEFERRED_REQUEST` with `index 4294967295`, +correctly — `defer_gc_request` reads it only while a root lock is held, so it +is not a fast-path read and never claims a slot. The list is what the fast +path reads, not what the module declares. diff --git a/crates/perry-runtime/src/arena/block.rs b/crates/perry-runtime/src/arena/block.rs index 6908be38da..96e64195d1 100644 --- a/crates/perry-runtime/src/arena/block.rs +++ b/crates/perry-runtime/src/arena/block.rs @@ -122,6 +122,29 @@ impl Drop for BlockPool { } } +// --------------------------------------------------------------------------- +// These three stay RAW, and not because nobody got to them: they are read from +// inside `Arena::new`, which runs as `ARENA`'s lazy initializer — and `ARENA` +// is the FIRST provider `tls_hot::fill` resolves. Routing them through the hot +// cache closes a cycle that ends in a stack overflow at thread start: +// +// HotKey::get -> hot() -> hot_uncached -> hot_via_tls +// -> (temp_roots still null) fill() +// -> arena_hot_addr() -> ARENA.with(..) -> Arena::new +// -> ARENA_TOTAL_BYTES / BLOCK_POOL{,_BYTES}.with(..) +// -> HotKey::get -> hot() -> ... (temp_roots STILL null) ... +// +// `fill` writes `temp_roots` last precisely so a re-entrant reader cannot see a +// half-filled cache as ready — which makes the nesting re-run `fill`, and +// re-run `ARENA`'s initializer, without bound. It bites on any thread where +// the first `hot()` precedes the first arena touch, i.e. every freshly spawned +// one, and it fails as a crash rather than as a slow path. +// +// The rule this is an instance of: a declaration read from the dynamic extent +// of a `tls_hot::fill` provider cannot use `crate::perry_thread_local!`. Today +// `ARENA` is the only provider whose initializer runs code, so this is the +// whole set. +// --------------------------------------------------------------------------- thread_local! { static BLOCK_POOL: RefCell = const { RefCell::new(BlockPool { blocks: Vec::new(), @@ -981,7 +1004,9 @@ thread_local! { /// alloc into a tombstone slot or the end, and release inside /// `arena_reset_empty_blocks`). pub(crate) static ARENA_TOTAL_BYTES: std::cell::Cell = const { std::cell::Cell::new(0) }; +} +crate::perry_thread_local! { /// Cached running sum of `block.offset` across the old-gen arena — /// the delta-maintained twin of `ARENA_TOTAL_BYTES` above, same /// rationale: `gc_budgeted_due_trigger()` reads the old-gen in-use @@ -1007,8 +1032,18 @@ thread_local! { /// the OldReclaim trigger. pub(crate) static OLD_GEN_IN_USE_BYTES: std::cell::Cell = const { std::cell::Cell::new(0) }; +} + +// `ARENA` and `INLINE_STATE` below stay raw: they are NAMED `HotTls` fields +// (`arena`, `inline_state`) whose addresses `tls_hot::fill` reads through the +// providers further down, and a named field is one dependent load cheaper +// than a claimed slot. Their neighbours in this block were never migrated. +thread_local! { pub(crate) static ARENA: UnsafeCell = UnsafeCell::new(Arena::new(HeapGeneration::Nursery, HeapSpace::NurseryEden)); +} + +crate::perry_thread_local! { /// Segregated long-lived arena (issue #179). Holds objects that are /// intentionally pinned for the lifetime of the program by explicit @@ -1056,6 +1091,9 @@ thread_local! { pub(crate) static OLD_ARENA: UnsafeCell = UnsafeCell::new(Arena::new_lazy(HeapGeneration::Old, HeapSpace::Old)); +} + +thread_local! { /// Inline allocator state — a cache of the current arena block's /// `(data, offset, size)` tuple, exposed via a stable pointer so /// codegen can emit inline bump-allocate IR without going through @@ -1072,6 +1110,12 @@ thread_local! { }) }; } +/// Hot-cache slot claimed by `OLD_GEN_IN_USE_BYTES`. See above. +#[cfg(test)] +pub(crate) fn old_gen_in_use_bytes_slot_index() -> u32 { + OLD_GEN_IN_USE_BYTES.slot_index() +} + // --- #7469 hot-TLS address providers. See `crate::tls_hot`. --- /// Address of this thread's `ARENA`. Resolving it once and caching it is what diff --git a/crates/perry-runtime/src/arena/mod.rs b/crates/perry-runtime/src/arena/mod.rs index 59ffb4cac6..fbc359df41 100644 --- a/crates/perry-runtime/src/arena/mod.rs +++ b/crates/perry-runtime/src/arena/mod.rs @@ -47,6 +47,8 @@ pub(crate) use block::{ /// allocation path uses instead of a per-access `_tlv_get_addr`. pub(crate) use block::{arena_hot_addr, hot_arena, hot_inline_state, inline_state_hot_addr}; #[cfg(test)] +pub(crate) use block::old_gen_in_use_bytes_slot_index; +#[cfg(test)] pub(crate) use block::{ block_pool_bytes_for_test, block_pool_explicit_drained_bytes_for_test, block_pool_put, force_next_block_alloc_failure, gc_trigger_arena_borrow_depth, gc_trigger_arena_calls, diff --git a/crates/perry-runtime/src/arena/page_meta.rs b/crates/perry-runtime/src/arena/page_meta.rs index bc6984dff3..0fe26f3c9b 100644 --- a/crates/perry-runtime/src/arena/page_meta.rs +++ b/crates/perry-runtime/src/arena/page_meta.rs @@ -319,13 +319,20 @@ pub(crate) struct OldArenaSourceBlockSelection { pub(crate) pages: crate::fast_hash::PtrHashSet, } +// `PAGE_GENERATIONS` and `PAGE_GENERATION_CACHE` stay raw because they are +// NAMED fields of `HotTls` (`page_generations`, `page_generation_cache`), whose +// providers below hand `tls_hot::fill` their addresses. A named field is one +// dependent load cheaper than a claimed slot, which is why the closed set +// exists; everything else in this file was simply never migrated. thread_local! { static PAGE_GENERATIONS: RefCell = RefCell::new(crate::fast_hash::new_ptr_hash_map()); static PAGE_GENERATION_CACHE: UnsafeCell = const { UnsafeCell::new(PageGenerationCacheSet::empty()) }; +} +crate::perry_thread_local! { static OLD_GEN_PAGE_OBJECTS: RefCell = RefCell::new(crate::fast_hash::new_ptr_hash_map()); @@ -1138,7 +1145,7 @@ pub(crate) fn register_old_object_pages(header_addr: usize, total_size: usize) { // which fails if a new toucher of either table appears without one. // --------------------------------------------------------------------------- -thread_local! { +crate::perry_thread_local! { /// Old-object page registrations not yet folded into `OLD_GEN_PAGE_OBJECTS`. /// Entries are `(header_addr, total_size)`; nothing here is dereferenced, so /// a deferred entry never keeps an object alive and is not a GC root — and diff --git a/crates/perry-runtime/src/gc/barrier/mod.rs b/crates/perry-runtime/src/gc/barrier/mod.rs index 808a66427c..a4bbd8e407 100644 --- a/crates/perry-runtime/src/gc/barrier/mod.rs +++ b/crates/perry-runtime/src/gc/barrier/mod.rs @@ -697,6 +697,10 @@ pub(super) unsafe fn scan_dirty_object_slots( // retained only as a test fallback for the previous object-level // HashSet behavior. +// The three declarations below stay raw: they are NAMED `HotTls` fields +// (`incremental_mark_valid_ptrs`, `birth_extra_flags`, +// `incremental_mark_minor_only`), one dependent load cheaper than a claimed +// slot. Everything after them was simply never migrated. thread_local! { /// Active incremental mark barrier state (Full AND budgeted Minor /// cycles — a Minor cycle sliced across mutator turns has exactly the @@ -737,7 +741,9 @@ thread_local! { /// later. Old children need no shading in a minor anyway: minors never /// collect live old-gen objects. pub(super) static INCREMENTAL_MARK_BARRIER_MINOR_ONLY: Cell = const { Cell::new(false) }; +} +crate::perry_thread_local! { /// Dirty old-generation pages that have received a YOUNG-gen /// pointer since the last collection. This is Perry's compact /// modbuf: barriers log bounded page regions, and minor GC scans @@ -1761,7 +1767,7 @@ fn mark_dirty_old_page_uncached(page: usize) -> bool { inserted } -thread_local! { +crate::perry_thread_local! { /// PERRY_GC_VERIFY_EVACUATION diagnostic only: every old page EVER marked /// dirty over the process lifetime (never cleared). Lets the verifier's /// missing-edge report say whether the slot's page was recorded at some diff --git a/crates/perry-runtime/src/gc/malloc.rs b/crates/perry-runtime/src/gc/malloc.rs index 7203300abf..df85b12362 100644 --- a/crates/perry-runtime/src/gc/malloc.rs +++ b/crates/perry-runtime/src/gc/malloc.rs @@ -196,7 +196,7 @@ pub(super) const MALLOC_STATE_HEAVY_LEN_THRESHOLD: usize = 64 * 1024; /// 7/8 load factor without further rehashes). pub(super) const MALLOC_STATE_HEAVY_CAPACITY: usize = 256 * 1024; -thread_local! { +crate::perry_thread_local! { pub(crate) static MALLOC_STATE: RefCell = RefCell::new(MallocState { objects: Vec::with_capacity(MALLOC_STATE_INITIAL_CAPACITY), set: crate::fast_hash::PtrHashSet::with_capacity_and_hasher( @@ -209,12 +209,25 @@ thread_local! { heavy_capacity_reserved: false, kind_telemetry: [MallocKindTelemetry::zero(); MALLOC_KIND_BUCKET_COUNT], }); +} +// `ARENA_FREE_LIST` / `ARENA_FREE_LIST_NONEMPTY` stay raw: they are NAMED +// `HotTls` fields (`arena_free_list`, `arena_free_list_nonempty`), one +// dependent load cheaper than a claimed slot. +thread_local! { pub(crate) static ARENA_FREE_LIST: RefCell> = const { RefCell::new(Vec::new()) }; pub(crate) static ARENA_FREE_LIST_NONEMPTY: std::cell::Cell = const { std::cell::Cell::new(false) }; } +/// Hot-cache slot claimed by `MALLOC_STATE`, whose length is the +/// `MallocCount` trigger's basis on every `gc_malloc`. Liveness +/// instrumentation for `gc::tests::trigger_path_tls`. +#[cfg(test)] +pub(crate) fn malloc_state_slot_index() -> u32 { + MALLOC_STATE.slot_index() +} + pub fn gc_malloc(size: usize, obj_type: u8) -> *mut u8 { let total = GC_HEADER_SIZE + size; let layout = Layout::from_size_align(total, 8).unwrap(); @@ -480,7 +493,7 @@ pub(super) fn malloc_sweep_revalidate_header( }) } -thread_local! { +crate::perry_thread_local! { pub(super) static MALLOC_REGISTRY_REBUILD_COUNT: Cell = const { Cell::new(0) }; } diff --git a/crates/perry-runtime/src/gc/old_free.rs b/crates/perry-runtime/src/gc/old_free.rs index 360a75c4c8..de64ba30e2 100644 --- a/crates/perry-runtime/src/gc/old_free.rs +++ b/crates/perry-runtime/src/gc/old_free.rs @@ -47,7 +47,7 @@ use super::*; -thread_local! { +crate::perry_thread_local! { /// total_size -> user_ptrs of swept holes of exactly that size. static OLD_FREE_MAP: RefCell>> = RefCell::new(crate::fast_hash::new_ptr_hash_map()); @@ -60,6 +60,14 @@ pub(crate) fn old_free_bytes() -> usize { OLD_FREE_BYTES.with(Cell::get) } +/// Hot-cache slot claimed by `OLD_FREE_BYTES`, which +/// `gc_budgeted_due_trigger` reads on every `gc_malloc`. Liveness +/// instrumentation for `gc::tests::trigger_path_tls`. +#[cfg(test)] +pub(crate) fn old_free_bytes_slot_index() -> u32 { + OLD_FREE_BYTES.slot_index() +} + fn old_free_push(user_ptr: usize, total_size: usize) { if user_ptr == 0 || total_size < GC_HEADER_SIZE { return; diff --git a/crates/perry-runtime/src/gc/policy.rs b/crates/perry-runtime/src/gc/policy.rs index 7088e39b65..12e089418d 100644 --- a/crates/perry-runtime/src/gc/policy.rs +++ b/crates/perry-runtime/src/gc/policy.rs @@ -5,7 +5,7 @@ pub(super) const GC_FLAG_IN_ALLOC: u8 = 0b01; /// Bit 1 of GC_FLAGS — suppression flag (JSON.parse). pub(super) const GC_FLAG_SUPPRESSED: u8 = 0b10; -thread_local! { +crate::perry_thread_local! { pub(super) static GC_FLAGS: Cell = const { Cell::new(0) }; } @@ -292,7 +292,7 @@ thread_local! { static GC_NURSERY_CAP_TEST_SUPPRESSED: Cell = const { Cell::new(false) }; } -thread_local! { +crate::perry_thread_local! { /// Lower bound for the next GC trigger. Bumped after each /// `gc_collect_inner` based on collection effectiveness (see the /// adaptive logic in `gc_check_trigger`). @@ -401,7 +401,7 @@ pub(super) const GC_MALLOC_COUNT_STEP_INITIAL: usize = 100_000; pub(super) const GC_MALLOC_COUNT_STEP_MAX: usize = 2_000_000; pub(super) const GC_MALLOC_COUNT_STEP_MIN: usize = 10_000; -thread_local! { +crate::perry_thread_local! { /// Per-program adaptive malloc-count step. Mirrors `GC_STEP_BYTES` /// behaviour: doubles when mostly-garbage, halves when mostly-live. pub(super) static GC_MALLOC_COUNT_STEP: std::cell::Cell = @@ -431,7 +431,7 @@ thread_local! { /// the Map/Set side-allocation finalizers) instead of leaking. const GC_EXTERNAL_SIDE_ALLOC_STEP: usize = 16 * 1024 * 1024; -thread_local! { +crate::perry_thread_local! { static GC_EXTERNAL_SIDE_ALLOC_PENDING: std::cell::Cell = const { std::cell::Cell::new(0) }; static GC_EXTERNAL_SIDE_LIVE_BYTES: std::cell::Cell = const { std::cell::Cell::new(0) }; } @@ -942,7 +942,7 @@ impl GcTriggerSnapshot { } } -thread_local! { +crate::perry_thread_local! { pub(super) static GC_DEFERRED_REQUEST: Cell = const { Cell::new(DeferredGcRequest::None) }; pub(super) static GC_OLD_RECLAIM_PENDING: Cell = const { Cell::new(false) }; @@ -2663,7 +2663,7 @@ enum BudgetedGcTrigger { MallocCount, } -thread_local! { +crate::perry_thread_local! { static GC_BUDGETED_CYCLE: RefCell> = const { RefCell::new(None) }; static GC_BUDGETED_CYCLE_ACTIVE: Cell = const { Cell::new(false) }; static GC_BUDGETED_STEP_ACTIVE: Cell = const { Cell::new(false) }; @@ -2698,6 +2698,56 @@ pub(super) fn gc_old_reclaim_debt_bytes(old_in_use: usize, baseline: usize) -> u old_in_use.saturating_sub(trigger) as u64 } +/// The thread-locals [`gc_budgeted_due_trigger`] reads on its fast path, with +/// the hot-cache slot each one claimed. +/// +/// Enumerating them in the module that reads them is what makes the coverage +/// test able to fail: reverting any declaration below to a raw +/// `thread_local!` removes `slot_index` and breaks this function's build at +/// the declaration's own name, rather than leaving a silently slow path — the +/// failure mode `tls_hot.rs` was written to abolish and that this path +/// nonetheless kept for three years. +#[cfg(test)] +pub(crate) fn trigger_path_hot_slot_indices() -> Vec<(&'static str, u32)> { + // Touch each one first: a slot is claimed on first read, not at + // declaration, so an unread declaration reports the unassigned sentinel. + // + // `GC_DEFERRED_REQUEST` is deliberately absent: `defer_gc_request` reads it + // only when `GC_ROOT_LOCK_DEPTH` is non-zero, so on the fast path it is + // never touched and never claims a slot. Listing it made this function's + // own test fail with `index 4294967295` on the first run — which is the + // evidence that the test can fail, and the reason the list is "what the + // fast path reads" rather than "what the module declares". + let _ = gc_budgeted_due_trigger(); + vec![ + ("GC_OLD_RECLAIM_PENDING", GC_OLD_RECLAIM_PENDING.slot_index()), + ( + "GC_LAST_OLD_RECLAIM_IN_USE_BYTES", + GC_LAST_OLD_RECLAIM_IN_USE_BYTES.slot_index(), + ), + ("GC_NEXT_MALLOC_TRIGGER", GC_NEXT_MALLOC_TRIGGER.slot_index()), + ("GC_NEXT_TRIGGER_BYTES", GC_NEXT_TRIGGER_BYTES.slot_index()), + ("GC_TRIGGER_ARMED", GC_TRIGGER_ARMED.slot_index()), + ( + "GC_EXTERNAL_SIDE_LIVE_BYTES", + GC_EXTERNAL_SIDE_LIVE_BYTES.slot_index(), + ), + ("GC_MAJOR_PACING_RETAINING", GC_MAJOR_PACING_RETAINING.slot_index()), + ("GC_FLAGS", GC_FLAGS.slot_index()), + ( + "GC_BUDGETED_CYCLE_ACTIVE", + GC_BUDGETED_CYCLE_ACTIVE.slot_index(), + ), + ("GC_BUDGETED_STEP_ACTIVE", GC_BUDGETED_STEP_ACTIVE.slot_index()), + ( + "OLD_GEN_IN_USE_BYTES", + crate::arena::old_gen_in_use_bytes_slot_index(), + ), + ("OLD_FREE_BYTES", super::old_free_bytes_slot_index()), + ("MALLOC_STATE", super::malloc_state_slot_index()), + ] +} + fn gc_budgeted_due_trigger() -> Option { let old_pending = GC_OLD_RECLAIM_PENDING.with(Cell::get); // #6010: external Map/Set side-buffer bytes escalate to OldReclaim too. diff --git a/crates/perry-runtime/src/gc/tenuring.rs b/crates/perry-runtime/src/gc/tenuring.rs index 4fd221261f..1abb9dd99c 100644 --- a/crates/perry-runtime/src/gc/tenuring.rs +++ b/crates/perry-runtime/src/gc/tenuring.rs @@ -152,7 +152,7 @@ const RAISE_DEBOUNCE_CYCLES: u8 = 2; /// adaptive threshold has eliminated the re-copying). const NURSERY_CAP_SCALE_MAX: u8 = 4; -thread_local! { +crate::perry_thread_local! { static TENURING_SURVIVALS: Cell = const { Cell::new(GC_TENURING_SURVIVALS_MAX) }; static RAISE_STREAK: Cell = const { Cell::new(0) }; /// Survival-rate lock: promote-on-first-copy until influx goes quiet. @@ -354,7 +354,7 @@ pub(super) fn note_surviving_object_census(moved_bytes: usize, moved_objects: us return; } OBJECT_CENSUS_SEEDED.with(|seeded| seeded.set(true)); - let previous = MEAN_SURVIVING_OBJECT_BYTES.replace(mean); + let previous = MEAN_SURVIVING_OBJECT_BYTES.with(|c| c.replace(mean)); if previous != mean && crate::gc::gc_diag_enabled() { eprintln!( "[gc-tenuring] nursery cap object denomination: mean_surviving_object_bytes {} -> {} \ @@ -417,7 +417,7 @@ pub(super) fn maybe_seed_object_census_from_allocation(from_space_in_use_bytes: if mean == 0 { return; } - let previous = MEAN_SURVIVING_OBJECT_BYTES.replace(mean); + let previous = MEAN_SURVIVING_OBJECT_BYTES.with(|c| c.replace(mean)); if crate::gc::gc_diag_enabled() { eprintln!( "[gc-tenuring] nursery cap object denomination: allocation census seeded \ diff --git a/crates/perry-runtime/src/gc/tests/mod.rs b/crates/perry-runtime/src/gc/tests/mod.rs index 186fe29187..8819176480 100644 --- a/crates/perry-runtime/src/gc/tests/mod.rs +++ b/crates/perry-runtime/src/gc/tests/mod.rs @@ -60,6 +60,8 @@ mod step_bounds; pub(super) mod support; mod survival_diag; mod teardown; +mod tls_fill_reentrancy; +mod trigger_path_tls; mod telemetry_verifier; mod temp_roots; mod triggers; diff --git a/crates/perry-runtime/src/gc/tests/tls_fill_reentrancy.rs b/crates/perry-runtime/src/gc/tests/tls_fill_reentrancy.rs new file mode 100644 index 0000000000..da66fb1113 --- /dev/null +++ b/crates/perry-runtime/src/gc/tests/tls_fill_reentrancy.rs @@ -0,0 +1,43 @@ +//! A thread whose FIRST act is resolving the hot-TLS cache must survive it. +//! +//! `tls_hot::fill`'s first provider is `arena_hot_addr()`, which initializes +//! `ARENA` — and `Arena::new` reads `ARENA_TOTAL_BYTES` and `BLOCK_POOL`. If +//! either of those were declared with `crate::perry_thread_local!`, the read +//! would go `HotKey::get -> hot() -> hot_uncached -> hot_via_tls`, find +//! `temp_roots` still null (`fill` writes it LAST, deliberately, so a +//! re-entrant reader cannot mistake a half-filled cache for a ready one), call +//! `fill` again, and re-run `ARENA`'s initializer — without bound. +//! +//! That failure is a stack overflow at thread start, not a slow path, and it +//! only appears on a thread where the first `hot()` precedes the first arena +//! touch. This test is that thread. It is the standing guard on the rule +//! recorded at those three declarations in `arena/block.rs`: a thread-local +//! read from inside the dynamic extent of a `fill` provider cannot use the +//! macro. +//! +//! Sabotage-proved: moving `ARENA_TOTAL_BYTES` alone into the +//! `crate::perry_thread_local!` block below it makes this test abort with +//! `thread '' has overflowed its stack / fatal runtime error: stack +//! overflow` (release, 2026-09-05). It is not a hypothetical. +#[test] +fn a_fresh_thread_may_resolve_the_hot_cache_before_touching_the_arena() { + // A small stack so an unbounded `fill` recursion aborts here rather than + // running long enough to look like a hang. + std::thread::Builder::new() + .stack_size(1 << 20) + .spawn(|| { + // `published_slots` reaches `hot()` and nothing else, so this is + // the first arena-touching call on the thread. + let _ = crate::tls_hot::published_slots(); + // And the arena still works afterwards: `fill` ran to completion + // rather than being abandoned mid-way by a recursion guard. + assert!( + crate::arena::arena_total_bytes() > 0, + "the arena reported no reserved bytes after `fill`, so \ + `ARENA`'s initializer did not complete on this thread" + ); + }) + .unwrap() + .join() + .expect("resolving the hot TLS cache first on a fresh thread panicked"); +} diff --git a/crates/perry-runtime/src/gc/tests/trigger_path_tls.rs b/crates/perry-runtime/src/gc/tests/trigger_path_tls.rs new file mode 100644 index 0000000000..e5791c4e8e --- /dev/null +++ b/crates/perry-runtime/src/gc/tests/trigger_path_tls.rs @@ -0,0 +1,67 @@ +//! The `gc_check_trigger` fast path must resolve its thread-locals through the +//! hot cache, not through `_tlv_get_addr`. +//! +//! `gc_check_trigger` runs on **every** `gc_malloc`. Measured with `sample` on +//! the compiled claude-code TUI streaming a 3300-char reply (14,580 main-thread +//! samples), `_tlv_get_addr` was 380 main-thread leaf samples and **85 of them +//! sat under `gc_budgeted_due_trigger < gc_check_trigger < gc_malloc`** — the +//! trigger predicate resolving `GC_OLD_RECLAIM_PENDING`, +//! `GC_LAST_OLD_RECLAIM_IN_USE_BYTES`, `GC_NEXT_MALLOC_TRIGGER`, +//! `GC_TRIGGER_ARMED`, `GC_NEXT_TRIGGER_BYTES`, `GC_EXTERNAL_SIDE_LIVE_BYTES`, +//! `OLD_GEN_IN_USE_BYTES`, `ARENA_TOTAL_BYTES`, `OLD_FREE_BYTES`, +//! `MALLOC_STATE` and the survivor cells one out-of-line call at a time. +//! +//! `scripts/check_thread_locals.py` cannot express this: it ratchets on the +//! number of raw `thread_local!` *blocks* per file, so `gc/policy.rs` read as +//! "6" while declaring 28 cold thread-locals, and the block count does not move +//! when a declaration is converted alongside a split (`arena/block.rs` went +//! from 11 cold declarations to 0 with its recorded count unchanged at 2). The +//! declaration-denominated ratchet added in the same change closes the *policy* +//! hole; this test is the *runtime* half — it asserts the mechanism is live on +//! the path the profile named. + +use crate::tls_hot::{published_slots, HOT_SLOT_CAPACITY}; + +/// Every trigger-path declaration named above must own a hot slot, and +/// driving `gc_check_trigger` must actually populate slots on the calling +/// thread. +/// +/// Two assertions, because they fail to different sabotage. Reverting one +/// declaration to a raw `thread_local!` removes `slot_index` and breaks the +/// build at the named line; making the slot allocator hand out its overflow +/// sentinel keeps the build green and trips the bound check below. +#[test] +fn gc_check_trigger_resolves_its_thread_locals_through_the_hot_cache() { + // A fresh thread so the "how many slots did this path publish" reading is + // this path's, not the harness's. + std::thread::spawn(|| { + let before = published_slots(); + crate::gc::gc_check_trigger(); + let after = published_slots(); + + for (name, idx) in crate::gc::policy::trigger_path_hot_slot_indices() { + assert!( + (idx as usize) < HOT_SLOT_CAPACITY, + "{name} did not claim a hot TLS slot (index {idx}, capacity \ + {HOT_SLOT_CAPACITY}); it is paying `_tlv_get_addr` per read on \ + every `gc_malloc`", + ); + } + + // The predicate reads at least the ten declarations listed in the + // module docs. A lower bound, deliberately: `gc_check_trigger` reaches + // other hot declarations too, and this must not fail when an unrelated + // one is added or removed. It DOES fail if the trigger path stops + // going through `HotKey` at all, which is the regression #7469 has + // already suffered three times. + let published = after - before; + assert!( + published >= 11, + "gc_check_trigger published only {published} hot TLS slots on a \ + fresh thread ({before} -> {after}); the trigger path's \ + thread-locals are resolving through `_tlv_get_addr` again", + ); + }) + .join() + .expect("trigger-path TLS probe thread panicked"); +} diff --git a/crates/perry-runtime/src/gc/trace.rs b/crates/perry-runtime/src/gc/trace.rs index fb350daa45..0232cc6a60 100644 --- a/crates/perry-runtime/src/gc/trace.rs +++ b/crates/perry-runtime/src/gc/trace.rs @@ -1,6 +1,6 @@ use super::*; -thread_local! { +crate::perry_thread_local! { /// Set by test-only helpers that wipe page metadata for isolation /// (`old_arena_page_index_clear_for_tests`): real objects become /// unclassifiable in that synthetic state, so the differential verifier @@ -85,7 +85,7 @@ pub(super) fn classifier_verify_enabled() -> bool { *CACHED.get_or_init(|| super::env_flag_enabled("PERRY_GC_VERIFY_CLASSIFIER")) } -thread_local! { +crate::perry_thread_local! { pub(super) static MARK_SEEDS: std::cell::UnsafeCell> = const { std::cell::UnsafeCell::new(Vec::new()) }; } @@ -922,7 +922,7 @@ pub(super) fn trace_marked_objects(valid_ptrs: &ValidPointerSet) { /// old-block neighbors as new block-persist candidates. pub(super) const BLOCK_PERSIST_WINDOW: usize = 5; -thread_local! { +crate::perry_thread_local! { /// Objects this thread's block-persistence pass has FORCE-MARKED since /// process start — i.e. kept alive for no reason other than sharing a /// block with something reachable. diff --git a/scripts/check_thread_locals.py b/scripts/check_thread_locals.py index b2db1a1481..bff22628f2 100755 --- a/scripts/check_thread_locals.py +++ b/scripts/check_thread_locals.py @@ -211,11 +211,23 @@ def cfg_test_module_files(root: Path, crates: list[str]) -> set[str]: return test_only -def shipping_raw_blocks(src: str) -> int: - """Raw `thread_local!` blocks that survive into a non-test build. +def shipping_raw_declarations(src: str) -> int: + """Raw `thread_local!` DECLARATIONS that survive into a non-test build. Skips a block carrying `#[cfg(test)]` directly above it, and any block inside an inline `#[cfg(test)] mod … { … }`. + + Declarations, not blocks. `_tlv_get_addr` is paid per *declaration* read, + and `thread_local! { … }` holds any number of them, so a block-denominated + ratchet cannot see the thing it exists to bound. Measured on this + repository at `d36a1af0c`: the 122 recorded cold BLOCKS were **339 cold + declarations**, `gc/policy.rs` alone recorded as 6 while declaring 28 — + and a `static` added to an already-recorded block passed the gate in + silence, which is how the allocation path re-accumulated the cost #7469 + removed. Counting declarations also makes the two halves of the report + commensurable: the hot side was already counted in declarations, so + "318 hot / 122 cold" was comparing unlike units and read as a 2.6:1 + majority for the mechanism when cold was in fact the larger number. """ gated_spans = [brace_span(src, m.start()) for m in CFG_TEST_INLINE_MOD_RE.finditer(src)] count = 0 @@ -226,12 +238,13 @@ def shipping_raw_blocks(src: str) -> int: line = preceding[preceding.rfind("\n") + 1 :].strip() if line == "#[cfg(test)]": continue - count += 1 + open_at, close_at = brace_span(src, m.start()) + count += len(DECL_RE.findall(src[open_at + 1 : close_at])) return count def scan(root: Path, crates: list[str]) -> tuple[dict[str, int], int]: - """Raw `thread_local!` blocks per file, and total hot declarations.""" + """Raw `thread_local!` declarations per file, and total hot declarations.""" raw: dict[str, int] = {} hot_declarations = 0 test_only_files = cfg_test_module_files(root, crates) @@ -249,7 +262,7 @@ def scan(root: Path, crates: list[str]) -> tuple[dict[str, int], int]: ) if rel in EXCLUDED or rel in test_only_files: continue - count = shipping_raw_blocks(src) + count = shipping_raw_declarations(src) if count: raw[rel] = count return raw, hot_declarations @@ -271,7 +284,7 @@ def verify(root: Path, crates: list[str], allowlist_path: Path) -> list[str]: for rel, count in sorted(raw.items()): if rel not in recorded: problems.append( - f"{rel}: {count} raw `thread_local!` block(s), none allowed.\n" + f"{rel}: {count} raw `thread_local!` declaration(s), none allowed.\n" f" Use `crate::perry_thread_local!` — same syntax, same " f"`.with()` at every call site, and the address lands in this " f"thread's hot cache instead of costing a `_tlv_get_addr` call " @@ -281,7 +294,7 @@ def verify(root: Path, crates: list[str], allowlist_path: Path) -> list[str]: elif recorded[rel] != count: direction = "gained" if count > recorded[rel] else "lost" problems.append( - f"{rel}: {direction} raw `thread_local!` blocks " + f"{rel}: {direction} raw `thread_local!` declarations " f"({recorded[rel]} recorded, {count} found). " f"Convert it, or run --update to re-record." ) @@ -290,7 +303,7 @@ def verify(root: Path, crates: list[str], allowlist_path: Path) -> list[str]: if rel not in raw: problems.append( f"{rel}: recorded as having {recorded[rel]} cold " - f"`thread_local!` block(s), but has none. A stale entry is one " + f"`thread_local!` declaration(s), but has none. A stale entry is one " f"nobody has to justify — delete it with --update." ) @@ -311,9 +324,11 @@ def write_allowlist(root: Path, crates: list[str], allowlist_path: Path) -> None json.dumps( { "_comment": ( - "Files still declaring raw `thread_local!`. Every entry is a " - "declaration that pays `_tlv_get_addr` on Darwin; the count is " - "a ratchet, so adding one to an already-listed file fails too. " + "Files still declaring raw `thread_local!`. The count is the " + "number of DECLARATIONS that survive into a shipping build — " + "each one pays `_tlv_get_addr` per read on Darwin — and it is a " + "ratchet, so adding a `static` to an already-listed file fails " + "whether or not it opens a new block. " "New code should use `crate::perry_thread_local!` — see " "crates/perry-runtime/src/tls_hot.rs. Regenerate with " "scripts/check_thread_locals.py --update." @@ -364,13 +379,30 @@ def self_test() -> int: failures.append("a new raw `thread_local!` in an unlisted file passed") (src_dir / "new.rs").unlink() - # 2. A second raw declaration in an already-listed file must fail. + # 2. A second raw declaration in an already-listed file must fail — + # in a NEW block… write_source(src_dir / "cold.rs", "thread_local! { static A: u8 = const { 0 }; }\n" "thread_local! { static D: u8 = const { 0 }; }\n" ) if not verify(root, CRATES, allowlist): - failures.append("a raw `thread_local!` added to a listed file passed") + failures.append("a raw `thread_local!` block added to a listed file passed") + + # 2b. …and, the half a block-denominated ratchet could not see, inside + # the block that is ALREADY recorded. This is how the allocation + # path re-accumulated the cost #7469 removed: `gc/policy.rs` + # declared 28 cold thread-locals while the allowlist read "6", and + # every one after the first was free to add. + write_source(src_dir / "cold.rs", + "thread_local! {\n" + " static A: u8 = const { 0 };\n" + " static D: u8 = const { 0 };\n" + "}\n" + ) + if not verify(root, CRATES, allowlist): + failures.append( + "a raw `static` added to a listed file's EXISTING block passed" + ) # 3. A stale entry must fail. write_source(src_dir / "cold.rs", @@ -429,7 +461,7 @@ def self_test() -> int: print(f"SELF-TEST FAILED: {f}", file=sys.stderr) if failures: return 1 - print("self-test: the checker can fail in all six directions") + print("self-test: the checker can fail in all seven directions") return 0 @@ -455,7 +487,7 @@ def main() -> int: raw, hot_declarations = scan(REPO, CRATES) print( f"thread-local policy OK: {hot_declarations} hot declarations, " - f"{sum(raw.values())} raw blocks in {len(raw)} recorded cold files, " + f"{sum(raw.values())} cold declarations in {len(raw)} recorded files, " f"capacity {hot_slot_capacity(REPO)}" ) return 0 diff --git a/scripts/gc_runtime_root_holders.json b/scripts/gc_runtime_root_holders.json index 0100899021..237b198a2c 100644 --- a/scripts/gc_runtime_root_holders.json +++ b/scripts/gc_runtime_root_holders.json @@ -294,7 +294,7 @@ "crates/perry-runtime/src/gc/census.rs": "388414f9629f196e84673e91bebd04bdcdcabdaa180252d2dfe4b82d1b49ca5a", "crates/perry-runtime/src/gc/cycle.rs": "2e2f5adca2229f74409e01a1cb571e2147cd8a33f58d0976711fce98d4777309", "crates/perry-runtime/src/gc/mod.rs": "7dd42b9506a97e6844fd3225dc53dfd59512631784750f58ff72208d68595481", - "crates/perry-runtime/src/gc/policy.rs": "fa8e9fa188d50bd92c3fbe23950a195906baf387f3a208497791bdd23f1c72db", + "crates/perry-runtime/src/gc/policy.rs": "2c49102ee846cae79bcb96a95a60b8804ac66a86617db8adb24859288e3cda30", "crates/perry-runtime/src/gc/progress.rs": "a5ad3971bbe4047229ca57325234780daa85921dbc778e1c08dff4ad07ccfb96" } } diff --git a/scripts/thread_local_cold_allowlist.json b/scripts/thread_local_cold_allowlist.json index 4744f170ac..9686b6e210 100644 --- a/scripts/thread_local_cold_allowlist.json +++ b/scripts/thread_local_cold_allowlist.json @@ -1,83 +1,79 @@ { - "_comment": "Files still declaring raw `thread_local!`. Every entry is a declaration that pays `_tlv_get_addr` on Darwin; the count is a ratchet, so adding one to an already-listed file fails too. New code should use `crate::perry_thread_local!` \u2014 see crates/perry-runtime/src/tls_hot.rs. Regenerate with scripts/check_thread_locals.py --update.", - "_hot_declarations": 287, + "_comment": "Files still declaring raw `thread_local!`. The count is the number of DECLARATIONS that survive into a shipping build \u2014 each one pays `_tlv_get_addr` per read on Darwin \u2014 and it is a ratchet, so adding a `static` to an already-listed file fails whether or not it opens a new block. New code should use `crate::perry_thread_local!` \u2014 see crates/perry-runtime/src/tls_hot.rs. Regenerate with scripts/check_thread_locals.py --update.", + "_hot_declarations": 385, "files": { "crates/perry-runtime/src/agent.rs": 1, - "crates/perry-runtime/src/arena/block.rs": 2, + "crates/perry-runtime/src/arena/block.rs": 5, "crates/perry-runtime/src/arena/page_meta.rs": 2, - "crates/perry-runtime/src/async_context.rs": 2, - "crates/perry-runtime/src/async_hooks.rs": 3, - "crates/perry-runtime/src/builtins/console.rs": 2, - "crates/perry-runtime/src/builtins/formatting.rs": 5, + "crates/perry-runtime/src/async_context.rs": 3, + "crates/perry-runtime/src/async_hooks.rs": 7, + "crates/perry-runtime/src/builtins/console.rs": 4, + "crates/perry-runtime/src/builtins/formatting.rs": 9, "crates/perry-runtime/src/builtins/formatting/boxed_primitives.rs": 1, - "crates/perry-runtime/src/builtins/globals.rs": 2, + "crates/perry-runtime/src/builtins/globals.rs": 4, "crates/perry-runtime/src/bun_ffi/types.rs": 1, "crates/perry-runtime/src/child_process/reactor.rs": 1, - "crates/perry-runtime/src/child_process/v8_serde.rs": 1, + "crates/perry-runtime/src/child_process/v8_serde.rs": 2, "crates/perry-runtime/src/closure/dispatch/errors.rs": 1, - "crates/perry-runtime/src/cluster.rs": 2, + "crates/perry-runtime/src/cluster.rs": 3, "crates/perry-runtime/src/dyn_eval/bridge.rs": 1, "crates/perry-runtime/src/dyn_eval/env.rs": 1, - "crates/perry-runtime/src/dyn_eval/mod.rs": 1, + "crates/perry-runtime/src/dyn_eval/mod.rs": 6, "crates/perry-runtime/src/eh.rs": 1, "crates/perry-runtime/src/eh_walker.rs": 1, - "crates/perry-runtime/src/error.rs": 2, + "crates/perry-runtime/src/error.rs": 3, "crates/perry-runtime/src/event_pump.rs": 1, - "crates/perry-runtime/src/fs/dir_glob_watch/watch.rs": 1, - "crates/perry-runtime/src/fs/filehandle.rs": 1, - "crates/perry-runtime/src/fs/mod.rs": 1, - "crates/perry-runtime/src/fs/stream.rs": 1, - "crates/perry-runtime/src/gc/barrier/mod.rs": 2, - "crates/perry-runtime/src/gc/barrier_arming.rs": 1, + "crates/perry-runtime/src/fs/dir_glob_watch/watch.rs": 7, + "crates/perry-runtime/src/fs/filehandle.rs": 6, + "crates/perry-runtime/src/fs/mod.rs": 6, + "crates/perry-runtime/src/fs/stream.rs": 4, + "crates/perry-runtime/src/gc/barrier/mod.rs": 3, + "crates/perry-runtime/src/gc/barrier_arming.rs": 3, "crates/perry-runtime/src/gc/cycle_malloc_trim.rs": 1, "crates/perry-runtime/src/gc/fromspace_scan.rs": 1, "crates/perry-runtime/src/gc/layout.rs": 1, - "crates/perry-runtime/src/gc/layout_tables.rs": 1, + "crates/perry-runtime/src/gc/layout_tables.rs": 3, "crates/perry-runtime/src/gc/malloc.rs": 2, - "crates/perry-runtime/src/gc/mod.rs": 2, - "crates/perry-runtime/src/gc/old_free.rs": 1, - "crates/perry-runtime/src/gc/policy.rs": 6, - "crates/perry-runtime/src/gc/promote_in_place.rs": 1, - "crates/perry-runtime/src/gc/roots/runtime_handles.rs": 1, + "crates/perry-runtime/src/gc/mod.rs": 3, + "crates/perry-runtime/src/gc/promote_in_place.rs": 11, + "crates/perry-runtime/src/gc/roots/runtime_handles.rs": 2, "crates/perry-runtime/src/gc/roots/scan_mode.rs": 1, "crates/perry-runtime/src/gc/roots/shadow_stack.rs": 2, "crates/perry-runtime/src/gc/roots/temp_roots.rs": 1, - "crates/perry-runtime/src/gc/scan_fallback.rs": 1, + "crates/perry-runtime/src/gc/scan_fallback.rs": 2, "crates/perry-runtime/src/gc/shape_install.rs": 1, - "crates/perry-runtime/src/gc/telemetry.rs": 2, - "crates/perry-runtime/src/gc/tenuring.rs": 1, - "crates/perry-runtime/src/gc/trace.rs": 3, + "crates/perry-runtime/src/gc/telemetry.rs": 3, "crates/perry-runtime/src/intl/number_format.rs": 1, "crates/perry-runtime/src/iter_result.rs": 1, - "crates/perry-runtime/src/json/mod.rs": 1, + "crates/perry-runtime/src/json/mod.rs": 12, "crates/perry-runtime/src/json/raw_json.rs": 1, "crates/perry-runtime/src/json_tape.rs": 2, - "crates/perry-runtime/src/json_tape_store.rs": 1, + "crates/perry-runtime/src/json_tape_store.rs": 3, "crates/perry-runtime/src/media_playback.rs": 1, - "crates/perry-runtime/src/native_arena.rs": 1, + "crates/perry-runtime/src/native_arena.rs": 3, "crates/perry-runtime/src/node_http2_constants.rs": 1, - "crates/perry-runtime/src/node_inspector.rs": 1, + "crates/perry-runtime/src/node_inspector.rs": 2, "crates/perry-runtime/src/node_repl.rs": 1, - "crates/perry-runtime/src/node_stream_constructors.rs": 2, - "crates/perry-runtime/src/node_stream_tests.rs": 1, - "crates/perry-runtime/src/node_submodules/blob.rs": 1, - "crates/perry-runtime/src/node_submodules/diagnostics.rs": 3, - "crates/perry-runtime/src/node_submodules/diagnostics_tail.rs": 1, - "crates/perry-runtime/src/node_submodules/mod.rs": 1, - "crates/perry-runtime/src/node_submodules/test.rs": 1, - "crates/perry-runtime/src/node_submodules/test_once_unit_tests.rs": 1, + "crates/perry-runtime/src/node_stream_constructors.rs": 3, + "crates/perry-runtime/src/node_stream_tests.rs": 23, + "crates/perry-runtime/src/node_submodules/blob.rs": 4, + "crates/perry-runtime/src/node_submodules/diagnostics.rs": 9, + "crates/perry-runtime/src/node_submodules/diagnostics_tail.rs": 2, + "crates/perry-runtime/src/node_submodules/mod.rs": 4, + "crates/perry-runtime/src/node_submodules/test.rs": 11, + "crates/perry-runtime/src/node_submodules/test_once_unit_tests.rs": 2, "crates/perry-runtime/src/node_submodules/test_property.rs": 1, - "crates/perry-runtime/src/node_submodules/trace_events.rs": 1, - "crates/perry-runtime/src/object/native_module/callable_exports.rs": 2, + "crates/perry-runtime/src/node_submodules/trace_events.rs": 8, + "crates/perry-runtime/src/object/native_module/callable_exports.rs": 3, "crates/perry-runtime/src/object/spill.rs": 1, "crates/perry-runtime/src/os/os_process_emitter.rs": 1, - "crates/perry-runtime/src/os_process_streams.rs": 1, - "crates/perry-runtime/src/perf_hooks.rs": 2, - "crates/perry-runtime/src/process.rs": 2, - "crates/perry-runtime/src/process/env_misc.rs": 3, + "crates/perry-runtime/src/os_process_streams.rs": 3, + "crates/perry-runtime/src/perf_hooks.rs": 11, + "crates/perry-runtime/src/process.rs": 12, + "crates/perry-runtime/src/process/env_misc.rs": 4, "crates/perry-runtime/src/process/permission.rs": 1, "crates/perry-runtime/src/process/report.rs": 1, - "crates/perry-runtime/src/proxy.rs": 1, + "crates/perry-runtime/src/proxy.rs": 5, "crates/perry-runtime/src/pty/reactor.rs": 1, "crates/perry-runtime/src/static_plugins.rs": 1, "crates/perry-runtime/src/timer.rs": 1, @@ -87,7 +83,7 @@ "crates/perry-runtime/src/util_promisify.rs": 1, "crates/perry-runtime/src/v8.rs": 2, "crates/perry-runtime/src/wasi.rs": 1, - "crates/perry-runtime/src/weakref.rs": 1, - "crates/perry-runtime/src/web_storage.rs": 1 + "crates/perry-runtime/src/weakref.rs": 2, + "crates/perry-runtime/src/web_storage.rs": 2 } } From 38aeb6a27dda9bee7ae9d762ae918e2a21b4c8c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sat, 5 Sep 2026 22:39:00 +0200 Subject: [PATCH 02/11] docs(changelog): rename the fragment to its PR number Claude-Session: https://claude.ai/code/session_014UZWia6L37DpA93VLtNK9m --- ...gc-trigger-path-hot-tls.md => 9827-gc-trigger-path-hot-tls.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog.d/{9830-gc-trigger-path-hot-tls.md => 9827-gc-trigger-path-hot-tls.md} (100%) diff --git a/changelog.d/9830-gc-trigger-path-hot-tls.md b/changelog.d/9827-gc-trigger-path-hot-tls.md similarity index 100% rename from changelog.d/9830-gc-trigger-path-hot-tls.md rename to changelog.d/9827-gc-trigger-path-hot-tls.md From 3926290f46ff1ffeaafc32c03ece12d8ace2c46e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sat, 5 Sep 2026 21:27:42 +0200 Subject: [PATCH 03/11] perf(gc): route the trigger path and dirty-page barrier through hot TLS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `gc_check_trigger` runs on every `gc_malloc`, and `gc_budgeted_due_trigger` resolved eleven raw `thread_local!` declarations one `_tlv_get_addr` call at a time. Measured with `sample` on the compiled claude-code TUI streaming a 3300-char reply (14,578 active main-thread samples, callers resolved by an explicit ancestor walk): `_tlv_get_addr` was 380 main-thread leaf samples, 71 of them with `gc_budgeted_due_trigger` as the immediate caller, 36 in `old_page_account_dirty_slots`, 31 in `scan_dirty_object_slots`, 27 in `gc_malloc_header_is_tracked`. Sixty-seven declarations move to `crate::perry_thread_local!`. Why they were still cold is a measurement bug in the gate, not an oversight: `scripts/check_thread_locals.py` ratchets on raw `thread_local!` BLOCKS per file, and a block holds any number of declarations — so `gc/policy.rs` counted as 6 while declaring 28, and adding a `static` to a recorded block passed silently. In the same unit as the hot side, main was 318 hot against 339 cold declarations. The gate now ratchets on declarations (385/272) and `--self-test` gained the direction that catches it. `ARENA_TOTAL_BYTES`, `BLOCK_POOL` and `BLOCK_POOL_BYTES` stay raw and say so: they are read from `Arena::new`, which runs as `tls_hot::fill`'s first provider, so a `HotKey` there re-enters `fill` — which has not yet written the `temp_roots` field it gates on — and re-runs `ARENA`'s initializer without bound. Claude-Session: https://claude.ai/code/session_014UZWia6L37DpA93VLtNK9m --- changelog.d/9830-gc-trigger-path-hot-tls.md | 55 +++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 changelog.d/9830-gc-trigger-path-hot-tls.md diff --git a/changelog.d/9830-gc-trigger-path-hot-tls.md b/changelog.d/9830-gc-trigger-path-hot-tls.md new file mode 100644 index 0000000000..e791ac3b7d --- /dev/null +++ b/changelog.d/9830-gc-trigger-path-hot-tls.md @@ -0,0 +1,55 @@ +**The GC trigger path and the dirty-page barrier stop paying `_tlv_get_addr` +per read, and the policy gate that let them stop paying it now counts the +thing it is bounding.** + +`gc_check_trigger` runs on every `gc_malloc`, and its predicate +(`gc_budgeted_due_trigger`) resolved eleven raw `thread_local!` declarations +one out-of-line call at a time. Measured with `sample` on the compiled +claude-code TUI streaming a 3300-char reply (14,578 active main-thread +samples, callers resolved by an explicit ancestor walk rather than +nearest-symbol labels): `_tlv_get_addr` was 380 main-thread leaf samples, +**71 of them with `gc_budgeted_due_trigger` as the immediate caller**, 36 in +`old_page_account_dirty_slots`, 31 in `scan_dirty_object_slots`, 27 in +`gc_malloc_header_is_tracked`. `crates/perry-runtime/src/tls_hot.rs` has +existed to abolish exactly this since #7469; the allocation path's *fields* +were covered and the trigger path never was. + +Sixty-seven declarations across `gc/policy.rs`, `gc/malloc.rs`, `gc/old_free.rs`, +`gc/tenuring.rs`, `gc/trace.rs`, `gc/barrier/mod.rs`, `arena/block.rs` and +`arena/page_meta.rs` move to `crate::perry_thread_local!` — same syntax, same +`.with()` at every call site, the address served from this thread's hot cache +instead of a libdyld call. + +**Why they were still cold is a measurement bug in the gate, not an oversight +anyone could have noticed.** `scripts/check_thread_locals.py` ratchets on the +number of raw `thread_local!` **blocks** per file, while `thread_local! { … }` +holds any number of declarations — so `gc/policy.rs` counted as **6** while +declaring **28**, and adding a `static` to an already-recorded block passed +the gate silently. Counted in the same unit as the hot side, `main` was **318 +hot declarations against 339 cold ones** — cold was the majority, reported as +a 2.6:1 minority. The gate now ratchets on declarations (`385 hot / 272 +cold`), and `--self-test` grew a seventh direction that fails when a `static` +is added to a recorded block; restoring the block count makes that case, and +only that case, fail. + +Three declarations stay deliberately raw and say so at their declaration: +`ARENA_TOTAL_BYTES`, `BLOCK_POOL` and `BLOCK_POOL_BYTES` are read from +`Arena::new`, which runs as `tls_hot::fill`'s **first** provider, so a +`HotKey` there re-enters `fill` — which by design has not yet written the +`temp_roots` field it gates on — and re-runs `ARENA`'s initializer without +bound. It is a stack overflow at thread start, not a slow path, and it is the +first documented instance of the rule that a declaration read from inside a +`fill` provider cannot use the macro. `gc::tests::tls_fill_reentrancy` is the +standing guard, and it is sabotage-proved: moving `ARENA_TOTAL_BYTES` alone +into the neighbouring hot block aborts that test with `fatal runtime error: +stack overflow`. + +`gc::tests::trigger_path_tls` is the runtime half of the gate: it drives +`gc_check_trigger` on a fresh thread and asserts every trigger-path +declaration owns a hot slot and that the path publishes slots at all. +Reverting any one of them to a raw `thread_local!` removes `slot_index` and +breaks the build at that declaration's own name. It is a test that can fail +and did: the first run rejected `GC_DEFERRED_REQUEST` with `index 4294967295`, +correctly — `defer_gc_request` reads it only while a root lock is held, so it +is not a fast-path read and never claims a slot. The list is what the fast +path reads, not what the module declares. From 11da89c2b8509137863230a86e98233d52773bb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sat, 5 Sep 2026 22:39:00 +0200 Subject: [PATCH 04/11] docs(changelog): rename the fragment to its PR number Claude-Session: https://claude.ai/code/session_014UZWia6L37DpA93VLtNK9m --- changelog.d/9830-gc-trigger-path-hot-tls.md | 55 --------------------- 1 file changed, 55 deletions(-) delete mode 100644 changelog.d/9830-gc-trigger-path-hot-tls.md diff --git a/changelog.d/9830-gc-trigger-path-hot-tls.md b/changelog.d/9830-gc-trigger-path-hot-tls.md deleted file mode 100644 index e791ac3b7d..0000000000 --- a/changelog.d/9830-gc-trigger-path-hot-tls.md +++ /dev/null @@ -1,55 +0,0 @@ -**The GC trigger path and the dirty-page barrier stop paying `_tlv_get_addr` -per read, and the policy gate that let them stop paying it now counts the -thing it is bounding.** - -`gc_check_trigger` runs on every `gc_malloc`, and its predicate -(`gc_budgeted_due_trigger`) resolved eleven raw `thread_local!` declarations -one out-of-line call at a time. Measured with `sample` on the compiled -claude-code TUI streaming a 3300-char reply (14,578 active main-thread -samples, callers resolved by an explicit ancestor walk rather than -nearest-symbol labels): `_tlv_get_addr` was 380 main-thread leaf samples, -**71 of them with `gc_budgeted_due_trigger` as the immediate caller**, 36 in -`old_page_account_dirty_slots`, 31 in `scan_dirty_object_slots`, 27 in -`gc_malloc_header_is_tracked`. `crates/perry-runtime/src/tls_hot.rs` has -existed to abolish exactly this since #7469; the allocation path's *fields* -were covered and the trigger path never was. - -Sixty-seven declarations across `gc/policy.rs`, `gc/malloc.rs`, `gc/old_free.rs`, -`gc/tenuring.rs`, `gc/trace.rs`, `gc/barrier/mod.rs`, `arena/block.rs` and -`arena/page_meta.rs` move to `crate::perry_thread_local!` — same syntax, same -`.with()` at every call site, the address served from this thread's hot cache -instead of a libdyld call. - -**Why they were still cold is a measurement bug in the gate, not an oversight -anyone could have noticed.** `scripts/check_thread_locals.py` ratchets on the -number of raw `thread_local!` **blocks** per file, while `thread_local! { … }` -holds any number of declarations — so `gc/policy.rs` counted as **6** while -declaring **28**, and adding a `static` to an already-recorded block passed -the gate silently. Counted in the same unit as the hot side, `main` was **318 -hot declarations against 339 cold ones** — cold was the majority, reported as -a 2.6:1 minority. The gate now ratchets on declarations (`385 hot / 272 -cold`), and `--self-test` grew a seventh direction that fails when a `static` -is added to a recorded block; restoring the block count makes that case, and -only that case, fail. - -Three declarations stay deliberately raw and say so at their declaration: -`ARENA_TOTAL_BYTES`, `BLOCK_POOL` and `BLOCK_POOL_BYTES` are read from -`Arena::new`, which runs as `tls_hot::fill`'s **first** provider, so a -`HotKey` there re-enters `fill` — which by design has not yet written the -`temp_roots` field it gates on — and re-runs `ARENA`'s initializer without -bound. It is a stack overflow at thread start, not a slow path, and it is the -first documented instance of the rule that a declaration read from inside a -`fill` provider cannot use the macro. `gc::tests::tls_fill_reentrancy` is the -standing guard, and it is sabotage-proved: moving `ARENA_TOTAL_BYTES` alone -into the neighbouring hot block aborts that test with `fatal runtime error: -stack overflow`. - -`gc::tests::trigger_path_tls` is the runtime half of the gate: it drives -`gc_check_trigger` on a fresh thread and asserts every trigger-path -declaration owns a hot slot and that the path publishes slots at all. -Reverting any one of them to a raw `thread_local!` removes `slot_index` and -breaks the build at that declaration's own name. It is a test that can fail -and did: the first run rejected `GC_DEFERRED_REQUEST` with `index 4294967295`, -correctly — `defer_gc_request` reads it only while a root lock is held, so it -is not a fast-path read and never claims a slot. The list is what the fast -path reads, not what the module declares. From c8659015e0eb0636f103d3da2ccb08c141719ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 6 Sep 2026 03:38:30 +0200 Subject: [PATCH 05/11] perf(gc): pre-size the per-minor dirty-scan covered set Rebuilt from empty on every minor and reaching ~1,000 entries, it walked hashbrown's growth ladder and paid a RawTable::reserve_rehash at each power-of-two boundary: 217 leaf samples in reserve_rehash on a 3300-char claude-code reply (1.5% of the turn), 111 under PtrHashSet::insert and the rest under run_copied_minor_attempt and restore_surviving_dirty_coverage. Same treatment and same justification as PREVIOUS_SURVIVOR_ESTIMATE next to it: the count is strongly autocorrelated between adjacent cycles, over-estimating costs only untouched reserved bytes, under-estimating falls back to ordinary growth, and the estimate is capped so one huge cycle cannot make every later one reserve unboundedly. [gc-dirty-covered] reports len/capacity/presized_to per minor so the mechanism is counted rather than assumed. Claude-Session: https://claude.ai/code/session_014UZWia6L37DpA93VLtNK9m --- crates/perry-runtime/src/fast_hash.rs | 8 +++++ crates/perry-runtime/src/gc/copying.rs | 47 +++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/crates/perry-runtime/src/fast_hash.rs b/crates/perry-runtime/src/fast_hash.rs index 3d021d1f17..9245637661 100644 --- a/crates/perry-runtime/src/fast_hash.rs +++ b/crates/perry-runtime/src/fast_hash.rs @@ -107,6 +107,14 @@ pub fn new_ptr_hash_set() -> PtrHashSet { HashSet::with_hasher(PtrHasher) } +/// The same, pre-sized. A set that is rebuilt from empty on every collection +/// pays a `RawTable::reserve_rehash` at each power-of-two boundary on the way +/// up, and the whole table is re-hashed and re-copied each time. +#[inline] +pub fn new_ptr_hash_set_with_capacity(cap: usize) -> PtrHashSet { + HashSet::with_capacity_and_hasher(cap, PtrHasher) +} + #[inline] pub fn new_ptr_hash_map() -> PtrHashMap { HashMap::with_hasher(PtrHasher) diff --git a/crates/perry-runtime/src/gc/copying.rs b/crates/perry-runtime/src/gc/copying.rs index b9f8519701..041416fd60 100644 --- a/crates/perry-runtime/src/gc/copying.rs +++ b/crates/perry-runtime/src/gc/copying.rs @@ -217,6 +217,23 @@ static PREVIOUS_SURVIVOR_ESTIMATE: std::sync::atomic::AtomicUsize = /// reserve 100 MB of pointers. const SURVIVOR_ESTIMATE_CAP: usize = 1 << 21; +/// Previous minor's dirty-scan covered-set size, for pre-sizing the next one. +/// Capped for the same reason as the survivor estimate: a one-off huge cycle +/// must not make every later cycle reserve unboundedly. +static PREVIOUS_DIRTY_COVERED_ESTIMATE: std::sync::atomic::AtomicUsize = + std::sync::atomic::AtomicUsize::new(0); + +pub(super) fn previous_dirty_covered_estimate() -> usize { + PREVIOUS_DIRTY_COVERED_ESTIMATE.load(std::sync::atomic::Ordering::Relaxed) +} + +pub(super) fn note_dirty_covered_for_presizing(count: usize) { + PREVIOUS_DIRTY_COVERED_ESTIMATE.store( + count.min(SURVIVOR_ESTIMATE_CAP), + std::sync::atomic::Ordering::Relaxed, + ); +} + pub(super) fn note_survivor_count_for_presizing(count: usize) { PREVIOUS_SURVIVOR_ESTIMATE.store( count.min(SURVIVOR_ESTIMATE_CAP), @@ -1383,7 +1400,20 @@ pub(super) fn run_copied_minor_attempt( let snapshot = remembered_dirty_snapshot(); // #9754: objects whose every slot the dirty scan visited in-body — the // post-cycle coverage restore skips them (see `scan_dirty_object_slots`). - let mut dirty_scan_covered = crate::fast_hash::new_ptr_hash_set(); + // #9835: this set is rebuilt from EMPTY on every minor and reaches ~1,000 + // entries (`[gc-restore-coverage] objects_skipped=1026..1116`), so it walked + // hashbrown's growth ladder and paid a `RawTable::reserve_rehash` at each + // power-of-two boundary — measured 217 leaf samples in `reserve_rehash` on a + // 3300-char claude-code reply (1.5 % of the turn), 111 of them under + // `PtrHashSet::insert` and the rest under this function and + // `restore_surviving_dirty_coverage`. + // + // Same treatment, and the same justification, as `PREVIOUS_SURVIVOR_ESTIMATE` + // above: the count is strongly autocorrelated between adjacent cycles (it is + // the same program in the same phase), over-estimating costs only untouched + // reserved bytes, and under-estimating falls back to ordinary growth. + let mut dirty_scan_covered = + crate::fast_hash::new_ptr_hash_set_with_capacity(previous_dirty_covered_estimate()); if !untraced { let _phase = super::pin::CopyingWalkPhaseGuard::enter("remembered_set"); let remembered_stats = scan_remembered_dirty_slots_copying( @@ -1647,6 +1677,21 @@ pub(super) fn run_copied_minor_attempt( if !collector.skip_remembering { restore_surviving_dirty_coverage(&snapshot, &dirty_scan_covered, "copying_minor"); } + // The mechanism, counted rather than assumed: with the pre-size working, + // `capacity` is already >= `len` on entry and hashbrown never grows the + // table, so `reserve_rehash` disappears from this path. A capacity that + // keeps climbing across minors would say the estimate is not tracking. + if crate::gc::gc_diag_enabled() { + eprintln!( + "[gc-dirty-covered] len={} capacity={} presized_to={}", + dirty_scan_covered.len(), + dirty_scan_covered.capacity(), + previous_dirty_covered_estimate(), + ); + } + note_dirty_covered_for_presizing(dirty_scan_covered.len()); + { + } let malloc_freed_bytes = if malloc_sweep_due { let phase_start = trace_phase_start(trace); let freed = sweep_malloc_objects(); From b2b92f90f88d2a26bda0a5658649eb59ab7ad88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 6 Sep 2026 04:29:42 +0200 Subject: [PATCH 06/11] perf(gc): pre-size the per-minor dirty-scan covered set Claude-Session: https://claude.ai/code/session_014UZWia6L37DpA93VLtNK9m --- changelog.d/9835-dirty-scan-presize.md | 36 ++++++++++++++++++++++++++ crates/perry-runtime/src/gc/copying.rs | 11 ++++++++ 2 files changed, 47 insertions(+) create mode 100644 changelog.d/9835-dirty-scan-presize.md diff --git a/changelog.d/9835-dirty-scan-presize.md b/changelog.d/9835-dirty-scan-presize.md new file mode 100644 index 0000000000..3900ecf0df --- /dev/null +++ b/changelog.d/9835-dirty-scan-presize.md @@ -0,0 +1,36 @@ +**The per-minor dirty-scan covered set is pre-sized instead of being rebuilt +from empty**, removing the hashbrown growth ladder every copying minor walked. + +`dirty_scan_covered` is created with `new_ptr_hash_set()` at the top of every +copying minor and filled during the dirty-slot scan. Measured with +`[gc-dirty-covered]` (added here), it reaches **~119,000 entries** on a +3300-character claude-code reply — not the ~1,000 the `[gc-restore-coverage]` +`objects_skipped` figure suggested — so it walked hashbrown's capacity ladder +(1,792 → 14,336 → 57,344 → 114,688 → 229,376) and paid a +`RawTable::reserve_rehash` at each boundary, re-hashing and re-copying the whole +table. `reserve_rehash` was **217 leaf samples, 1.49 % of the turn**, 111 of +them under `PtrHashSet::insert` and the rest under `run_copied_minor_attempt` +and `restore_surviving_dirty_coverage`. + +The set is now pre-sized from the previous minor's count, the same treatment and +the same justification as `PREVIOUS_SURVIVOR_ESTIMATE` immediately above it: the +count is autocorrelated between adjacent cycles, over-estimating costs only +untouched reserved bytes, under-estimating falls back to ordinary growth, and +the estimate shares that constant's cap so one huge cycle cannot make every +later cycle reserve unboundedly. + +`reserve_rehash` falls **217 → 167 leaf samples (1.49 % → 1.24 % of the turn)**. +The rig is flat, as expected of a 1.5 % item — 400-character turn CPU 4.05 min +against 4.14, 3300 17.86 against 17.71 — with settled footprint and peak RSS +improving at 400 (557 → 457 MB, 604 → 563 MB) and flat at 3300. The ground +claimed is **work permanently removed**, counted rather than inferred: +`[gc-dirty-covered]` reports `len`, `capacity` and `presized_to` per minor, so +the pre-size can be seen tracking rather than assumed to. + +**A high-water estimate was tried and rejected.** It is better on the mechanism +— under-shoots fall from 57 of 96 minors to 21 of 97 — but reserving the peak on +every minor cost settled footprint 763 → 1165 MB and peak RSS 974 → 1250 MB at +3300 characters for no measurable time difference (167 vs 182 leaf samples, +inside run-to-run noise). Trading footprint for CPU is rejected, and here it did +not even buy CPU. The rejection is recorded at the function so the next person +does not re-derive it. diff --git a/crates/perry-runtime/src/gc/copying.rs b/crates/perry-runtime/src/gc/copying.rs index 041416fd60..84d64e17e4 100644 --- a/crates/perry-runtime/src/gc/copying.rs +++ b/crates/perry-runtime/src/gc/copying.rs @@ -227,6 +227,17 @@ pub(super) fn previous_dirty_covered_estimate() -> usize { PREVIOUS_DIRTY_COVERED_ESTIMATE.load(std::sync::atomic::Ordering::Relaxed) } +/// LAST-VALUE, and a high-water mark was tried and REJECTED. +/// +/// `[gc-dirty-covered]` shows this set is far more volatile than the survivor +/// count this pattern was copied from: it ramps 1,028 -> ~119,000 over a turn +/// and swings between adjacent minors, so a last-value estimate under-shoots on +/// 57 of 96 minors. A high-water mark fixes that on the mechanism — under-shoots +/// fall to 21 of 97 — and was still rejected: reserving the peak on EVERY minor +/// cost settled footprint 763 -> 1165 MB and peak RSS 974 -> 1250 MB at 3300 +/// characters, for no measurable time difference (`reserve_rehash` 167 vs 182 +/// leaf samples, inside run-to-run noise). Trading footprint for CPU is +/// rejected, and here it did not even buy CPU. pub(super) fn note_dirty_covered_for_presizing(count: usize) { PREVIOUS_DIRTY_COVERED_ESTIMATE.store( count.min(SURVIVOR_ESTIMATE_CAP), From 848cd5be6ca931d4b81424d530092935897c08f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 6 Sep 2026 04:32:39 +0200 Subject: [PATCH 07/11] docs(gc): name the rejected 'reserve the peak' idea at the function Claude-Session: https://claude.ai/code/session_014UZWia6L37DpA93VLtNK9m --- crates/perry-runtime/src/gc/copying.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/perry-runtime/src/gc/copying.rs b/crates/perry-runtime/src/gc/copying.rs index 84d64e17e4..b5bb5bd577 100644 --- a/crates/perry-runtime/src/gc/copying.rs +++ b/crates/perry-runtime/src/gc/copying.rs @@ -227,6 +227,9 @@ pub(super) fn previous_dirty_covered_estimate() -> usize { PREVIOUS_DIRTY_COVERED_ESTIMATE.load(std::sync::atomic::Ordering::Relaxed) } +/// LAST-VALUE. **Do not "just reserve the peak" — that was tried and it cost +/// 400 MB of settled footprint for no time gain.** +/// /// LAST-VALUE, and a high-water mark was tried and REJECTED. /// /// `[gc-dirty-covered]` shows this set is far more volatile than the survivor From 3ab14c246d93f104c0ffa552a3db490d923aa3fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 6 Sep 2026 03:57:53 +0000 Subject: [PATCH 08/11] fix(gc-ratchet): restore probe liveness and report scheduled failures --- .github/workflows/gc-ratchet.yml | 32 ++- .github/workflows/test.yml | 2 +- benchmarks/gc_ratchet/gc_ratchet.py | 6 +- .../probes/10_store_receiver_across_alloc.ts | 18 +- scripts/report_gc_ratchet.py | 183 ++++++++++++++++++ tests/test_gc_ratchet.py | 23 +++ tests/test_report_gc_ratchet.py | 157 +++++++++++++++ 7 files changed, 417 insertions(+), 4 deletions(-) create mode 100644 scripts/report_gc_ratchet.py create mode 100644 tests/test_report_gc_ratchet.py diff --git a/.github/workflows/gc-ratchet.yml b/.github/workflows/gc-ratchet.yml index 1c7ee38640..f99ace093a 100644 --- a/.github/workflows/gc-ratchet.yml +++ b/.github/workflows/gc-ratchet.yml @@ -144,7 +144,7 @@ jobs: # that coupling one planted defect shape at a time. - name: Harness unit tests and artifact validation run: | - python3 -m unittest discover -s tests -p 'test_gc_ratchet.py' -v + python3 -m unittest discover -s tests -p 'test*gc_ratchet.py' -v python3 benchmarks/gc_ratchet/gc_ratchet.py validate --scope structural - name: Decide whether this change can affect the collector @@ -270,3 +270,33 @@ jobs: .bench-results/gc-ratchet-current.json benchmarks/gc_ratchet/baseline/gc-ratchet-v1.json retention-days: 90 + + report-main-result: + needs: gc-ratchet + if: >- + always() && github.ref == 'refs/heads/main' && + (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && + (needs.gc-ratchet.result == 'failure' || needs.gc-ratchet.result == 'success') + runs-on: ubuntu-latest + timeout-minutes: 5 + # Serialize the short issue update, not the expensive measurement jobs. + concurrency: + group: gc-ratchet-main-alert + cancel-in-progress: false + permissions: + contents: read + actions: read + issues: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + persist-credentials: false + - name: Update the main GC ratchet incident + env: + GH_TOKEN: ${{ github.token }} + REPOSITORY: ${{ github.repository }} + RUN_ID: ${{ github.run_id }} + RESULT: ${{ needs.gc-ratchet.result }} + run: >- + python3 scripts/report_gc_ratchet.py + --repository "$REPOSITORY" --run-id "$RUN_ID" --result "$RESULT" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d8cbb74102..fc7a369410 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1589,7 +1589,7 @@ jobs: python scripts/check_gc_doc_claims.py python scripts/check_thread_locals.py --self-test python scripts/check_thread_locals.py - python -m unittest discover -s tests -p 'test_gc_ratchet.py' -v + python -m unittest discover -s tests -p 'test*gc_ratchet.py' -v python benchmarks/gc_ratchet/gc_ratchet.py validate --scope structural - name: Install Rust toolchain diff --git a/benchmarks/gc_ratchet/gc_ratchet.py b/benchmarks/gc_ratchet/gc_ratchet.py index 52cd1a41df..f64bbb0e0c 100644 --- a/benchmarks/gc_ratchet/gc_ratchet.py +++ b/benchmarks/gc_ratchet/gc_ratchet.py @@ -518,11 +518,15 @@ def distribution(values: Sequence[float]) -> dict[str, Any]: raise RatchetError("distribution has no samples") if any(not math.isfinite(value) for value in samples): raise RatchetError("distribution has a non-finite sample") + # Derive the summary from exactly the samples persisted below. Otherwise + # rounding timing samples can change spread_pct (or even the median) when + # validate_artifact recomputes it after a JSON round trip (#9834). + samples = [_clean(value) for value in samples] ordered = sorted(samples) median = statistics.median(ordered) spread = ordered[-1] - ordered[0] return { - "samples": [_clean(value) for value in samples], + "samples": samples, "sample_count": len(samples), "median": _clean(median), "min": _clean(ordered[0]), diff --git a/benchmarks/gc_ratchet/probes/10_store_receiver_across_alloc.ts b/benchmarks/gc_ratchet/probes/10_store_receiver_across_alloc.ts index ba1e98be34..78583b8a1a 100644 --- a/benchmarks/gc_ratchet/probes/10_store_receiver_across_alloc.ts +++ b/benchmarks/gc_ratchet/probes/10_store_receiver_across_alloc.ts @@ -33,7 +33,23 @@ declare function gc(): void; const SLOTS = 1024; -const ITERATIONS = 200000; + +// Keep margin above the nursery threshold (#9829, #9832, #9833). +// At 200,000 iterations this probe became inert after allocation optimizations: +// main @ d36a1af0c runs zero minors, despite still producing correct stdout. +// Measured with that compiler (three repeats per size): +// +// iterations minor_cycles +// 200,000 0 +// 600,000 2 +// 1,200,000 4 +// 2,400,000 9 +// +// The last size leaves >=2 minors after a further fourfold allocation reduction. +// Keep minor_cycles >=2 when resizing; the harness's zero-minor rejection is +// only a last line of defence. Removing the allocating RHS at this size still +// gives minor_cycles = copied_objects = freed_bytes = 0 (sabotage control). +const ITERATIONS = 2400000; // (1) module-level, so the receiver is loaded from a global handle rather than // a shadow slot. diff --git a/scripts/report_gc_ratchet.py b/scripts/report_gc_ratchet.py new file mode 100644 index 0000000000..722ca97c7c --- /dev/null +++ b/scripts/report_gc_ratchet.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +"""Keep one open incident for failed main-line GC ratchet runs (#9829). + +Only the reporting job receives issues:write. PR code never invokes this path. +The measurement/check exit status remains the GC gate's verdict. +""" +from __future__ import annotations + +import argparse +import json +import re +import subprocess + +MARKER = "" +STATE_RE = re.compile(r"") +TITLE = "GC ratchet is failing on main" + + +class GitHub: + def api(self, path, *, method="GET", payload=None, raw=False): + command = ["gh", "api", path, "--method", method] + if payload is not None: + command += ["--input", "-"] + result = subprocess.run( + command, input=json.dumps(payload) if payload is not None else None, + capture_output=True, text=True, check=True, + ) + return result.stdout if raw else json.loads(result.stdout) + + def pages(self, path, key=None): + separator = "&" if "?" in path else "?" + page = 1 + while True: + data = self.api(f"{path}{separator}per_page=100&page={page}") + entries = data[key] if key else data + yield from entries + if len(entries) < 100: + return + page += 1 + + +def eligible(run): + return run["head_branch"] == "main" and run["event"] in { + "schedule", "workflow_dispatch", + } + + +def state_from(body): + match = STATE_RE.search(body or "") + return json.loads(match[1]) if match else {} + + +def regression_rows(log): + rows = {} + for line in log.splitlines(): + start = line.find("| " + chr(96)) + if start < 0: + continue + row = line[start:].strip() + fields = [field.strip() for field in row.strip("|").split("|")] + if len(fields) == 8 and fields[-1] == "REGRESSION": + key = fields[0].strip(chr(96)) + "." + fields[1] + rows[key] = row + return rows + + +def successful_runs(client, base, run): + return [ + previous for previous in client.pages( + f"{base}/actions/workflows/{run['workflow_id']}/runs?branch=main&status=success", + "workflow_runs", + ) if eligible(previous) + ] + + +def report(client, repository, run_id, result): + base = f"repos/{repository}" + run = client.api(f"{base}/actions/runs/{run_id}") + if not eligible(run) or result not in {"success", "failure"}: + return None + issue = next(( + entry for entry in client.pages(f"{base}/issues?state=open&creator=github-actions%5Bbot%5D") + if not entry.get("pull_request") and MARKER in (entry.get("body") or "") + ), None) + old = state_from(issue["body"]) if issue else {} + order = (run["id"], run.get("run_attempt", 1)) + if order <= (old.get("run_id", 0), old.get("attempt", 0)): + return None + if result == "success": + if issue is None: + return None + body = issue["body"] + ( + f"\n\nRecovered in [run {run_id}]({run['html_url']}) " + f"at {run['head_sha']}.\n" + ) + return client.api( + f"{base}/issues/{issue['number']}", method="PATCH", + payload={"body": body, "state": "closed", "state_reason": "completed"}, + ) + + successes = successful_runs(client, base, run) + # A delayed failure must not reopen an incident already recovered by a + # newer run. The reporting job itself is serialized to avoid create races. + if any(previous["id"] > run["id"] for previous in successes): + return None + previous_green = next((p for p in successes if p["id"] < run["id"]), None) + green = ( + {"sha": previous_green["head_sha"], "url": previous_green["html_url"]} + if previous_green else old.get("last_green") + ) + jobs = list(client.pages( + f"{base}/actions/runs/{run_id}/attempts/{run.get('run_attempt', 1)}/jobs", "jobs", + )) + rows, failures, log_errors = {}, [], [] + for job in jobs: + if job["conclusion"] != "failure": + continue + failures.append(f"- [{job['name']}]({job['html_url']})") + failures.extend( + " - " + step["name"] for step in job.get("steps", []) + if step["conclusion"] == "failure" + ) + try: + rows.update(regression_rows(client.api( + f"{base}/actions/jobs/{job['id']}/logs", raw=True, + ))) + except subprocess.CalledProcessError: + log_errors.append(f"Logs unavailable for {job['name']}; follow the job link.") + + previous_rows = set(old.get("rows", [])) + added, cleared = sorted(set(rows) - previous_rows), sorted(previous_rows - set(rows)) + state = { + "run_id": run["id"], "attempt": run.get("run_attempt", 1), + "rows": sorted(rows), "last_green": green, + } + lines = [ + MARKER, TITLE, "", + f"Latest failure: [run {run_id}]({run['html_url']}) at {run['head_sha']}.", + f"Event: {run['event']}.", + ( + f"Last green: [{green['sha']}]({green['url']})." + if green else "Last green: unavailable in retained workflow history." + ), + "", "Failing jobs/steps:", *failures, "", + f"Regression cells: {len(rows)}.", + ] + if issue and rows and previous_rows: + lines += [ + f"Since the previous failure: {len(added)} added, {len(cleared)} cleared.", + "Added: " + (", ".join(added) or "none"), + "Cleared: " + (", ".join(cleared) or "none"), + ] + if rows: + lines += [ + "", "| Probe | Metric | Baseline | Current | Delta | Allowance | Gating | Status |", + "|---|---|---:|---:|---:|---:|:---:|---|", + *[rows[key] for key in sorted(rows)], + ] + else: + lines += ["No regression table was emitted; this is a setup/harness failure, not a clean measurement."] + lines += [ + "", *log_errors, + "", "Investigate changes since the last green; do not re-pin unexplained drift.", + "This issue is updated on failure and closed after a successful main-line run.", + "", "", + ] + payload = {"title": TITLE, "body": "\n".join(lines)} + if issue: + return client.api(f"{base}/issues/{issue['number']}", method="PATCH", payload=payload) + return client.api(f"{base}/issues", method="POST", payload=payload) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--repository", required=True) + parser.add_argument("--run-id", required=True, type=int) + parser.add_argument("--result", required=True) + args = parser.parse_args() + report(GitHub(), args.repository, args.run_id, args.result) + + +if __name__ == "__main__": + main() diff --git a/tests/test_gc_ratchet.py b/tests/test_gc_ratchet.py index 0066a63970..db778a95c6 100644 --- a/tests/test_gc_ratchet.py +++ b/tests/test_gc_ratchet.py @@ -326,6 +326,29 @@ def test_array_growth_exclusions_match_the_recorded_samples(self): self.assertEqual(entry["evidence"]["observed_spread"], max(values) - min(values)) +class DistributionRoundTripTests(unittest.TestCase): + def test_summaries_survive_json_round_trip(self): + # Timings arrive at higher precision than the six decimals we persist. + # Validation must derive exactly the summary written by measurement. + samples = [ + [27.105318423, 27.418923761, 28.075891629], + [0.0000004, 0.0000014, 0.0000024], + [1.0000001, 1.0000008], + [100, 200, 300], + ] + for values in samples: + with self.subTest(values=values), tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / "measurement.json" + path.write_text(json.dumps(distribution(values)), encoding="utf-8") + stored = json.loads(path.read_text(encoding="utf-8")) + self.assertEqual(stored, distribution(stored["samples"])) + + def test_non_finite_samples_are_still_rejected(self): + for value in (float("nan"), float("inf"), -float("inf")): + with self.subTest(value=value), self.assertRaises(RatchetError): + distribution([1, value, 3]) + + class ParsingTests(unittest.TestCase): def test_measurement_refuses_a_host_without_wait4_before_launching(self): with mock.patch.object(os, "wait4", None, create=True): diff --git a/tests/test_report_gc_ratchet.py b/tests/test_report_gc_ratchet.py new file mode 100644 index 0000000000..17522137e5 --- /dev/null +++ b/tests/test_report_gc_ratchet.py @@ -0,0 +1,157 @@ +"""Exercise incident lifecycle without posting to GitHub.""" +import copy +import subprocess +import unittest +from pathlib import Path + +from scripts.report_gc_ratchet import MARKER, regression_rows, report, state_from + +REPO = "PerryTS/perry" +TICK = chr(96) + + +def row(probe, metric="copied_bytes"): + return f"| {TICK}{probe}{TICK} | {metric} | 100 | 200 | +100% | 5 | yes | REGRESSION |" + + +class FakeGitHub: + def __init__(self): + self.run = { + "id": 200, "run_attempt": 1, "workflow_id": 10, + "head_sha": "b" * 40, "head_branch": "main", "event": "schedule", + "html_url": "https://github.com/PerryTS/perry/actions/runs/200", + } + self.issues = [] + self.successes = [ + {**self.run, "id": 100, "head_sha": "a" * 40, + "html_url": "https://github.com/PerryTS/perry/actions/runs/100"}, + ] + self.log = "2026-09-06T00:00:00Z " + row("01_probe") + self.log_error = False + self.writes = [] + + def api(self, path, *, method="GET", payload=None, raw=False): + if method != "GET": + self.writes.append((method, path, copy.deepcopy(payload))) + issue = {"number": 99, **payload} + self.issues = [] if payload.get("state") == "closed" else [issue] + return issue + if path.endswith("/logs"): + if self.log_error: + raise subprocess.CalledProcessError(1, ["gh"]) + return self.log + if path == f"repos/{REPO}/actions/runs/{self.run['id']}": + return self.run + raise AssertionError(path) + + def pages(self, path, key=None): + if "/issues?" in path: + return iter(self.issues) + if "/workflows/" in path: + return iter(self.successes) + if path.endswith("/jobs"): + return iter([{ + "id": 50, "name": "gc-ratchet", "conclusion": "failure", + "html_url": self.run["html_url"] + "/job/50", + "steps": [{"name": "Check against the pinned baseline", "conclusion": "failure"}], + }]) + raise AssertionError(path) + + +class IncidentTests(unittest.TestCase): + def test_first_failure_creates_one_issue_with_evidence(self): + client = FakeGitHub() + report(client, REPO, 200, "failure") + method, _, payload = client.writes[0] + self.assertEqual(method, "POST") + self.assertIn("a" * 40, payload["body"]) + self.assertIn("b" * 40, payload["body"]) + self.assertIn(row("01_probe"), payload["body"]) + self.assertIn("Check against the pinned baseline", payload["body"]) + self.assertEqual(state_from(payload["body"])["rows"], ["01_probe.copied_bytes"]) + + def test_repeated_failure_updates_and_reports_added_and_cleared_cells(self): + client = FakeGitHub() + report(client, REPO, 200, "failure") + client.run["id"] = 201 + client.log = row("02_probe") + report(client, REPO, 201, "failure") + method, path, payload = client.writes[-1] + self.assertEqual((method, path), ("PATCH", f"repos/{REPO}/issues/99")) + self.assertIn("1 added, 1 cleared", payload["body"]) + self.assertIn("Added: 02_probe.copied_bytes", payload["body"]) + self.assertIn("Cleared: 01_probe.copied_bytes", payload["body"]) + self.assertEqual(len(client.issues), 1) + + def test_recovery_closes_existing_issue_and_fresh_green_does_nothing(self): + client = FakeGitHub() + report(client, REPO, 200, "success") + self.assertEqual(client.writes, []) + report(client, REPO, 200, "failure") + client.run["id"] = 201 + report(client, REPO, 201, "success") + self.assertEqual(client.writes[-1][2]["state"], "closed") + self.assertIn("Recovered in", client.writes[-1][2]["body"]) + + def test_unmeasured_failure_and_missing_logs_still_open_incident(self): + for missing in (False, True): + with self.subTest(missing=missing): + client = FakeGitHub() + client.log, client.log_error = "", missing + report(client, REPO, 200, "failure") + body = client.writes[0][2]["body"] + self.assertIn("not a clean measurement", body) + if missing: + self.assertIn("Logs unavailable", body) + + def test_pr_tag_cancelled_and_unrelated_dispatch_do_not_write(self): + for event, branch, result in [ + ("pull_request", "main", "failure"), + ("push", "v1.0", "failure"), + ("workflow_dispatch", "topic", "failure"), + ("schedule", "main", "cancelled"), + ("schedule", "main", "skipped"), + ]: + with self.subTest(event=event, branch=branch, result=result): + client = FakeGitHub() + client.run.update(event=event, head_branch=branch) + report(client, REPO, 200, result) + self.assertEqual(client.writes, []) + + def test_retry_is_idempotent_and_older_success_cannot_close_newer_failure(self): + client = FakeGitHub() + report(client, REPO, 200, "failure") + report(client, REPO, 200, "failure") + client.run["id"] = 199 + report(client, REPO, 199, "success") + self.assertEqual(len(client.writes), 1) + client.run.update(id=200, run_attempt=2) + report(client, REPO, 200, "success") + self.assertEqual(client.writes[-1][2]["state"], "closed") + + def test_delayed_failure_cannot_reopen_after_newer_success(self): + client = FakeGitHub() + client.successes.insert(0, {**client.run, "id": 201}) + report(client, REPO, 200, "failure") + self.assertEqual(client.writes, []) + + def test_parser_ignores_non_gating_and_non_regression_rows(self): + valid = row("01_probe") + log = "\n".join([ + valid, valid.replace("REGRESSION", "ok"), + valid.replace("REGRESSION", "recorded"), + "compiler failure", + ]) + self.assertEqual(regression_rows(log), {"01_probe.copied_bytes": valid}) + + def test_workflow_keeps_writes_out_of_measurement_job(self): + path = Path(__file__).resolve().parents[1] / ".github/workflows/gc-ratchet.yml" + workflow = path.read_text(encoding="utf-8") + measurement, reporting = workflow.split(" report-main-result:", 1) + self.assertNotIn("issues: write", measurement) + self.assertIn("needs: gc-ratchet", reporting) + self.assertIn("issues: write", reporting) + self.assertIn("always()", reporting) + self.assertIn("refs/heads/main", reporting) + self.assertIn("github.event_name == 'schedule'", reporting) + self.assertIn("-p 'test*gc_ratchet.py'", measurement) From 31085027ae09aab1d63a3bb516e69097d27295b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 6 Sep 2026 04:21:47 +0000 Subject: [PATCH 09/11] fix(gc-ratchet): recover the baseline with per-cell evidence --- benchmarks/gc_ratchet/README.md | 13 + .../gc_ratchet/baseline/gc-ratchet-v1.json | 1058 +++- .../gc_ratchet/evidence/9829-recovery.json | 4931 +++++++++++++++++ .../gc_ratchet/evidence/9829-recovery.md | 176 + benchmarks/gc_ratchet/tolerances.json | 20 +- changelog.d/9829-gc-ratchet-recovery.md | 1 + tests/test_gc_ratchet.py | 50 +- 7 files changed, 5992 insertions(+), 257 deletions(-) create mode 100644 benchmarks/gc_ratchet/evidence/9829-recovery.json create mode 100644 benchmarks/gc_ratchet/evidence/9829-recovery.md create mode 100644 changelog.d/9829-gc-ratchet-recovery.md diff --git a/benchmarks/gc_ratchet/README.md b/benchmarks/gc_ratchet/README.md index 909fb2130d..88484e6864 100644 --- a/benchmarks/gc_ratchet/README.md +++ b/benchmarks/gc_ratchet/README.md @@ -691,3 +691,16 @@ failure this suite has paid for most often. | `run_gc_ratchet_baseline.sh` | quiet-host driver (`--check` / `--pin`) | | `../../tests/test_gc_ratchet.py` | proves the gate fails on each failure mode | | `../../.github/workflows/gc-ratchet.yml` | CI wiring | + + +## Recovery from the unwatched main failures (#9829) + +The [recovery record](evidence/9829-recovery.md) explains the selective counter +acceptance, repaired receiver-store probe, 21-run large-Eden capacity exclusion, +historical A/Bs, and evidence grades. Only accepted deterministic distributions +move; the original quiet-host RSS/time pin remains separate. + +Scheduled and main-branch dispatch runs now open or update one GC ratchet issue +on failure, including the run/SHA, last green, failing rows and their delta. +The next successful main-line run closes it. Reporting has a separate job and +does not swallow the measurement/check exit status. diff --git a/benchmarks/gc_ratchet/baseline/gc-ratchet-v1.json b/benchmarks/gc_ratchet/baseline/gc-ratchet-v1.json index 3d6db44e37..26d505e85f 100644 --- a/benchmarks/gc_ratchet/baseline/gc-ratchet-v1.json +++ b/benchmarks/gc_ratchet/baseline/gc-ratchet-v1.json @@ -121,7 +121,10 @@ "2026-08-08 re-pin every probe's conservative reading equals its precise one", "(gc_ratchet.py classify, excess 0.00% on all twelve), so heap_used_bytes now", "means what it says. `classify` is still the first step on a breach -- it is", - "now also the check that the term has not come back." + "now also the check that the term has not come back.", + "#9829 excludes only 13_large_eden_survivors.heap_total_bytes after 21 executions", + "of one binary varied by 2 MiB of reserved capacity with identical live bytes", + "and all seven GC counters. This is not a wider band or a retention exemption." ], "shared_ci": { "heap_used_bytes": { @@ -317,10 +320,22 @@ "issue": "https://github.com/PerryTS/perry/issues/9790" } } + }, + "13_large_eden_survivors": { + "heap_total_bytes": { + "gating": false, + "rationale": "NOT GATED ON THIS PROBE (#9829). Total reserved arena capacity varies by 2 MiB across executions of one unchanged binary: 18/21 runs reserve 61865984 bytes and 3/21 reserve 63963136. All 21 report the same 227048 live bytes and identical seven GC counters (including three minors and 377569 copied objects), with Node-equivalent output. js_arena_stats sums reserved block sizes for heapTotal, separately from its exact live-object census for heapUsed. This is spare-capacity/placement variation, not changing live retention or GC work; the precise allocating block/order is not isolated. CI run 33989581881 independently contains the same two capacities. Keep the cell visible, while heapUsed, liveness, copying/promotion/reclamation, and all other probes remain gated. Raw 21-run evidence and binary/source hashes: evidence/9829-recovery.json.", + "evidence": { + "observed_runs": 21, + "observed_spread": 2097152, + "measured_on": "2026-09-06, linux-x86_64, clean main d36a1af0c compiler and matching runtime/stdlib archives. One unchanged probe binary, 21 traced executions with PERRY_GC_SCAVENGE_NURSERY_MB=64. See evidence/9829-recovery.json capacity_variance.", + "issue": "https://github.com/PerryTS/perry/issues/9829" + } + } } } }, - "notes": "Re-pinned for #8122-recover: the allocation census before minor #0 object-denominates the first nursery cap (first cycle fires earlier on small-object workloads), one descriptor lookup per traced object, untraced-promotion threshold 990 -> 980. Every GC-accounting fingerprint shifts; retention improves on 12_large_live_set (-75%) and 13_large_eden_survivors moves +85 KB because its cycle 0 now holds up an in-place promotion at 581 permille (main at cap 49 retains 651 KB the same way).", + "notes": "Re-pinned for #8122-recover: the allocation census before minor #0 object-denominates the first nursery cap (first cycle fires earlier on small-object workloads), one descriptor lookup per traced object, untraced-promotion threshold 990 -> 980. Every GC-accounting fingerprint shifts; retention improves on 12_large_live_set (-75%) and 13_large_eden_survivors moves +85 KB because its cycle 0 now holds up an in-place promotion at 581 permille (main at cap 49 retains 651 KB the same way).\n\nRecovery of #9829 after scheduled main failures went unwatched from 2026-08-18 to 2026-09-06. Main d36a1af0c205ebdc7cf7f75b351ea34b5bc0fc0b / Actions run 33989581881 had 43 breached cells and an inert probe 10. The set moved 43 -> 48 -> 43 with intervening harness failures; this is one sample of a moving set, not cumulative debt. Accept only the 38 other breached deterministic cells plus 8 cells changed by the included #9833 probe repair. The repaired heap_used cell is already within its original band and is not re-pinned. All 126 original deterministic medians match macOS CI on the remote Linux host; all 46 accepted distributions are stable across the pinning repeats. Evidence grades are explicit: pairwise measurements and window-bracketed changes are not interchangeable. No bands, other deterministic values, RSS, peak RSS, or timing values change. Only 13.heap_total_bytes is excluded after 21 identical-binary runs show 2 MiB reserved-capacity variation with constant live bytes and all GC counters. See evidence/9829-recovery.json and evidence/9829-recovery.md; pinned_host timing for the resized workload requires a separate capture on its original quiet host.", "probes": { "01_nursery_churn": { "stdout": "probe:01_nursery_churn\nchecksum:-1399701504\n", @@ -449,26 +464,26 @@ }, "copied_objects": { "samples": [ - 6354, - 6354 + 4977, + 4977 ], "sample_count": 2, - "median": 6354, - "min": 6354, - "max": 6354, + "median": 4977, + "min": 4977, + "max": 4977, "stdev": 0, "spread": 0, "spread_pct": 0 }, "copied_bytes": { "samples": [ - 430008, - 430008 + 234536, + 234536 ], "sample_count": 2, - "median": 430008, - "min": 430008, - "max": 430008, + "median": 234536, + "min": 234536, + "max": 234536, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -501,13 +516,13 @@ }, "freed_bytes": { "samples": [ - 13201144, - 13201144 + 11299648, + 11299648 ], "sample_count": 2, - "median": 13201144, - "min": 13201144, - "max": 13201144, + "median": 11299648, + "min": 11299648, + "max": 11299648, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -654,13 +669,13 @@ }, "copied_bytes": { "samples": [ - 1780080, - 1780080 + 1450992, + 1450992 ], "sample_count": 2, - "median": 1780080, - "min": 1780080, - "max": 1780080, + "median": 1450992, + "min": 1450992, + "max": 1450992, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -885,13 +900,13 @@ }, "freed_bytes": { "samples": [ - 11304368, - 11304368 + 9240008, + 9240008 ], "sample_count": 2, - "median": 11304368, - "min": 11304368, - "max": 11304368, + "median": 9240008, + "min": 9240008, + "max": 9240008, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -1025,39 +1040,39 @@ }, "copied_objects": { "samples": [ - 450, - 450 + 3622, + 3622 ], "sample_count": 2, - "median": 450, - "min": 450, - "max": 450, + "median": 3622, + "min": 3622, + "max": 3622, "stdev": 0, "spread": 0, "spread_pct": 0 }, "copied_bytes": { "samples": [ - 25832, - 25832 + 149400, + 149400 ], "sample_count": 2, - "median": 25832, - "min": 25832, - "max": 25832, + "median": 149400, + "min": 149400, + "max": 149400, "stdev": 0, "spread": 0, "spread_pct": 0 }, "promoted_objects": { "samples": [ - 10, - 10 + 682, + 682 ], "sample_count": 2, - "median": 10, - "min": 10, - "max": 10, + "median": 682, + "min": 682, + "max": 682, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -1077,13 +1092,13 @@ }, "freed_bytes": { "samples": [ - 83880432, - 83880432 + 73346416, + 73346416 ], "sample_count": 2, - "median": 83880432, - "min": 83880432, - "max": 83880432, + "median": 73346416, + "min": 73346416, + "max": 73346416, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -1217,65 +1232,65 @@ }, "copied_objects": { "samples": [ - 5272, - 5272 + 20079, + 20079 ], "sample_count": 2, - "median": 5272, - "min": 5272, - "max": 5272, + "median": 20079, + "min": 20079, + "max": 20079, "stdev": 0, "spread": 0, "spread_pct": 0 }, "copied_bytes": { "samples": [ - 233384, - 233384 + 743384, + 743384 ], "sample_count": 2, - "median": 233384, - "min": 233384, - "max": 233384, + "median": 743384, + "min": 743384, + "max": 743384, "stdev": 0, "spread": 0, "spread_pct": 0 }, "promoted_objects": { "samples": [ - 0, - 0 + 3390, + 3390 ], "sample_count": 2, - "median": 0, - "min": 0, - "max": 0, + "median": 3390, + "min": 3390, + "max": 3390, "stdev": 0, "spread": 0, "spread_pct": 0 }, "promoted_bytes": { "samples": [ - 0, - 0 + 122120, + 122120 ], "sample_count": 2, - "median": 0, - "min": 0, - "max": 0, + "median": 122120, + "min": 122120, + "max": 122120, "stdev": 0, "spread": 0, "spread_pct": 0 }, "freed_bytes": { "samples": [ - 32422176, - 32422176 + 26862320, + 26862320 ], "sample_count": 2, - "median": 32422176, - "min": 32422176, - "max": 32422176, + "median": 26862320, + "min": 26862320, + "max": 26862320, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -1409,65 +1424,65 @@ }, "copied_objects": { "samples": [ - 11138, - 11138 + 298, + 298 ], "sample_count": 2, - "median": 11138, - "min": 11138, - "max": 11138, + "median": 298, + "min": 298, + "max": 298, "stdev": 0, "spread": 0, "spread_pct": 0 }, "copied_bytes": { "samples": [ - 682240, - 682240 + 72288, + 72288 ], "sample_count": 2, - "median": 682240, - "min": 682240, - "max": 682240, + "median": 72288, + "min": 72288, + "max": 72288, "stdev": 0, "spread": 0, "spread_pct": 0 }, "promoted_objects": { "samples": [ - 4960, - 4960 + 14, + 14 ], "sample_count": 2, - "median": 4960, - "min": 4960, - "max": 4960, + "median": 14, + "min": 14, + "max": 14, "stdev": 0, "spread": 0, "spread_pct": 0 }, "promoted_bytes": { "samples": [ - 248512, - 248512 + 1072, + 1072 ], "sample_count": 2, - "median": 248512, - "min": 248512, - "max": 248512, + "median": 1072, + "min": 1072, + "max": 1072, "stdev": 0, "spread": 0, "spread_pct": 0 }, "freed_bytes": { "samples": [ - 75244808, - 75244808 + 68136816, + 68136816 ], "sample_count": 2, - "median": 75244808, - "min": 75244808, - "max": 75244808, + "median": 68136816, + "min": 68136816, + "max": 68136816, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -1485,36 +1500,36 @@ "metrics": { "heap_used_bytes": { "samples": [ - 984840, - 984840, - 984840, - 984840, - 984840, - 984840, - 984840 + 1072456, + 1072456, + 1072456, + 1072456, + 1072456, + 1072456, + 1072456 ], "sample_count": 7, - "median": 984840, - "min": 984840, - "max": 984840, + "median": 1072456, + "min": 1072456, + "max": 1072456, "stdev": 0, "spread": 0, "spread_pct": 0 }, "heap_total_bytes": { "samples": [ - 23068672, - 23068672, - 23068672, - 23068672, - 23068672, - 23068672, - 23068672 + 25165824, + 25165824, + 25165824, + 25165824, + 25165824, + 25165824, + 25165824 ], "sample_count": 7, - "median": 23068672, - "min": 23068672, - "max": 23068672, + "median": 25165824, + "min": 25165824, + "max": 25165824, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -1601,13 +1616,13 @@ }, "copied_objects": { "samples": [ - 6387, - 6387 + 4991, + 4991 ], "sample_count": 2, - "median": 6387, - "min": 6387, - "max": 6387, + "median": 4991, + "min": 4991, + "max": 4991, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -1793,13 +1808,13 @@ }, "copied_objects": { "samples": [ - 3017, - 3017 + 2489, + 2489 ], "sample_count": 2, - "median": 3017, - "min": 3017, - "max": 3017, + "median": 2489, + "min": 2489, + "max": 2489, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -1985,52 +2000,52 @@ }, "copied_objects": { "samples": [ - 0, - 0 + 4962, + 4962 ], "sample_count": 2, - "median": 0, - "min": 0, - "max": 0, + "median": 4962, + "min": 4962, + "max": 4962, "stdev": 0, "spread": 0, "spread_pct": 0 }, "copied_bytes": { "samples": [ - 0, - 0 + 230352, + 230352 ], "sample_count": 2, - "median": 0, - "min": 0, - "max": 0, + "median": 230352, + "min": 230352, + "max": 230352, "stdev": 0, "spread": 0, "spread_pct": 0 }, "promoted_objects": { "samples": [ - 6240, - 6240 + 0, + 0 ], "sample_count": 2, - "median": 6240, - "min": 6240, - "max": 6240, + "median": 0, + "min": 0, + "max": 0, "stdev": 0, "spread": 0, "spread_pct": 0 }, "promoted_bytes": { "samples": [ - 421800, - 421800 + 0, + 0 ], "sample_count": 2, - "median": 421800, - "min": 421800, - "max": 421800, + "median": 0, + "min": 0, + "max": 0, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -2051,7 +2066,7 @@ } }, "10_store_receiver_across_alloc": { - "stdout": "probe:10_store_receiver_across_alloc\nchecksum:204277248\n", + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:-1837890048\n", "correctness": { "status": "pass", "oracle_version": "v26.5.1", @@ -2079,18 +2094,18 @@ }, "heap_total_bytes": { "samples": [ - 12582912, - 12582912, - 12582912, - 12582912, - 12582912, - 12582912, - 12582912 + 16777216, + 16777216, + 16777216, + 16777216, + 16777216, + 16777216, + 16777216 ], "sample_count": 7, - "median": 12582912, - "min": 12582912, - "max": 12582912, + "median": 16777216, + "min": 16777216, + "max": 16777216, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -2151,91 +2166,91 @@ }, "minor_cycles": { "samples": [ - 1, - 1 + 9, + 9 ], "sample_count": 2, - "median": 1, - "min": 1, - "max": 1, + "median": 9, + "min": 9, + "max": 9, "stdev": 0, "spread": 0, "spread_pct": 0 }, "step_cycles": { "samples": [ - 1, - 1 + 9, + 9 ], "sample_count": 2, - "median": 1, - "min": 1, - "max": 1, + "median": 9, + "min": 9, + "max": 9, "stdev": 0, "spread": 0, "spread_pct": 0 }, "copied_objects": { "samples": [ - 8160, - 8160 + 18660, + 18660 ], "sample_count": 2, - "median": 8160, - "min": 8160, - "max": 8160, + "median": 18660, + "min": 18660, + "max": 18660, "stdev": 0, "spread": 0, "spread_pct": 0 }, "copied_bytes": { "samples": [ - 506200, - 506200 + 825520, + 825520 ], "sample_count": 2, - "median": 506200, - "min": 506200, - "max": 506200, + "median": 825520, + "min": 825520, + "max": 825520, "stdev": 0, "spread": 0, "spread_pct": 0 }, "promoted_objects": { "samples": [ - 0, - 0 + 4722, + 4722 ], "sample_count": 2, - "median": 0, - "min": 0, - "max": 0, + "median": 4722, + "min": 4722, + "max": 4722, "stdev": 0, "spread": 0, "spread_pct": 0 }, "promoted_bytes": { "samples": [ - 0, - 0 + 228440, + 228440 ], "sample_count": 2, - "median": 0, - "min": 0, - "max": 0, + "median": 228440, + "min": 228440, + "max": 228440, "stdev": 0, "spread": 0, "spread_pct": 0 }, "freed_bytes": { "samples": [ - 8930928, - 8930928 + 88858544, + 88858544 ], "sample_count": 2, - "median": 8930928, - "min": 8930928, - "max": 8930928, + "median": 88858544, + "min": 88858544, + "max": 88858544, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -2369,26 +2384,26 @@ }, "copied_objects": { "samples": [ - 6245, - 6245 + 4975, + 4975 ], "sample_count": 2, - "median": 6245, - "min": 6245, - "max": 6245, + "median": 4975, + "min": 4975, + "max": 4975, "stdev": 0, "spread": 0, "spread_pct": 0 }, "copied_bytes": { "samples": [ - 422744, - 422744 + 230872, + 230872 ], "sample_count": 2, - "median": 422744, - "min": 422744, - "max": 422744, + "median": 230872, + "min": 230872, + "max": 230872, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -2574,13 +2589,13 @@ }, "copied_bytes": { "samples": [ - 2851312, - 2851312 + 2327008, + 2327008 ], "sample_count": 2, - "median": 2851312, - "min": 2851312, - "max": 2851312, + "median": 2327008, + "min": 2327008, + "max": 2327008, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -2600,26 +2615,26 @@ }, "promoted_bytes": { "samples": [ - 25395472, - 25395472 + 21201208, + 21201208 ], "sample_count": 2, - "median": 25395472, - "min": 25395472, - "max": 25395472, + "median": 21201208, + "min": 21201208, + "max": 21201208, "stdev": 0, "spread": 0, "spread_pct": 0 }, "freed_bytes": { "samples": [ - 76315456, - 76315456 + 63732784, + 63732784 ], "sample_count": 2, - "median": 76315456, - "min": 76315456, - "max": 76315456, + "median": 63732784, + "min": 63732784, + "max": 63732784, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -2755,65 +2770,65 @@ }, "copied_objects": { "samples": [ - 0, - 0 + 377569, + 377569 ], "sample_count": 2, - "median": 0, - "min": 0, - "max": 0, + "median": 377569, + "min": 377569, + "max": 377569, "stdev": 0, "spread": 0, "spread_pct": 0 }, "copied_bytes": { "samples": [ - 0, - 0 + 21429712, + 21429712 ], "sample_count": 2, - "median": 0, - "min": 0, - "max": 0, + "median": 21429712, + "min": 21429712, + "max": 21429712, "stdev": 0, "spread": 0, "spread_pct": 0 }, "promoted_objects": { "samples": [ - 541614, - 541614 + 386271, + 386271 ], "sample_count": 2, - "median": 541614, - "min": 541614, - "max": 541614, + "median": 386271, + "min": 386271, + "max": 386271, "stdev": 0, "spread": 0, "spread_pct": 0 }, "promoted_bytes": { "samples": [ - 30316904, - 30316904 + 21777792, + 21777792 ], "sample_count": 2, - "median": 30316904, - "min": 30316904, - "max": 30316904, + "median": 21777792, + "min": 21777792, + "max": 21777792, "stdev": 0, "spread": 0, "spread_pct": 0 }, "freed_bytes": { "samples": [ - 97086288, - 97086288 + 108243696, + 108243696 ], "sample_count": 2, - "median": 97086288, - "min": 97086288, - "max": 97086288, + "median": 108243696, + "min": 108243696, + "max": 108243696, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -2950,13 +2965,13 @@ }, "copied_objects": { "samples": [ - 307, - 307 + 330, + 330 ], "sample_count": 2, - "median": 307, - "min": 307, - "max": 307, + "median": 330, + "min": 330, + "max": 330, "stdev": 0, "spread": 0, "spread_pct": 0 @@ -5389,5 +5404,552 @@ "memory_ratio": 0.288906 } } + }, + "accepted_deterministic_deltas": { + "commit": "3e3e652e007ac853bf934daa86fa36c870b0e3e2", + "code_tree": "ea7b395f07b51662a4c94ae5a16e61a5f7b5aeaa", + "generated_at": "2026-09-06T04:12:57+00:00", + "measurement": { + "platform": "linux-x86_64", + "repeats": 7, + "traced_runs": 2, + "binaries": { + "perry": { + "size": 117268864, + "sha256": "6fde7dcbb9be714705cc0ebe84e5ab8a0549a1b0e12796fc294c7e872fe32cf0" + }, + "libperry_runtime.a": { + "size": 108253612, + "sha256": "9abf7b9fa1e94f84e6bd1ff4f40c662633491d9267482ebd848e7aa32d14c802" + }, + "libperry_stdlib.a": { + "size": 244536102, + "sha256": "b4e9a0f948da340032c83fa958f959071a2f7fc5428cf899acca9019a0dee2ab" + } + } + }, + "notes": "Recovery of #9829 after scheduled main failures went unwatched from 2026-08-18 to 2026-09-06. Main d36a1af0c205ebdc7cf7f75b351ea34b5bc0fc0b / Actions run 33989581881 had 43 breached cells and an inert probe 10. The set moved 43 -> 48 -> 43 with intervening harness failures; this is one sample of a moving set, not cumulative debt. Accept only the 38 other breached deterministic cells plus 8 cells changed by the included #9833 probe repair. The repaired heap_used cell is already within its original band and is not re-pinned. All 126 original deterministic medians match macOS CI on the remote Linux host; all 46 accepted distributions are stable across the pinning repeats. Evidence grades are explicit: pairwise measurements and window-bracketed changes are not interchangeable. No bands, other deterministic values, RSS, peak RSS, or timing values change. Only 13.heap_total_bytes is excluded after 21 identical-binary runs show 2 MiB reserved-capacity variation with constant live bytes and all GC counters. See evidence/9829-recovery.json and evidence/9829-recovery.md; pinned_host timing for the resized workload requires a separate capture on its original quiet host.", + "causes": { + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140": { + "pull_request": 8313, + "category": "measured: smaller common objects", + "evidence": "First-accrual A/B recorded in #9829: 19/19 CI regression cells reproduced byte-exact at dc4bcf287 -> e2e6f4a1c. ObjectHeader and two-field objects shrink, reducing byte traffic and changing nursery occupancy. https://github.com/PerryTS/perry/issues/9829#issuecomment-5555657705" + }, + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b": { + "pull_request": 8657, + "category": "measured: array growth targets keep their generation", + "evidence": "Fresh remote builds of parent c2da03439 and 06e1ab349 confirm the 04/05 copy/promotion jumps, 07 heap capacity increase, and 14 copied_objects 340 -> 330. Old array growth targets are born tenured and young growth avoids an intervening collection. Full deterministic samples are in evidence/9829-recovery.json." + }, + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a": { + "pull_request": 8806, + "category": "measured: cache exact shape transitions", + "evidence": "Fresh parent 066f9d595 / b3e88f1fb builds reproduce all four Aug-26 added CI cells exactly: 01 copied_objects=6684; 06 copied_objects=11714; 09 promoted_objects=6560; 11 copied_objects=6562. This cache changes the metadata root population; later changes also move these same cells." + }, + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a": { + "pull_request": 8887, + "category": "bracketed: Aug-27/28 allocation and rooting specializations", + "evidence": "Candidate within 41e8479a5..6d10e8a1d, not isolated to this commit. The window includes #8878/#8887/#8888 array and call specialization, #8891 metadata edges, and #8900 weak transition targets. By measured d02892491, all five 06_string_retention cells already equal present main exactly (298 copied, 72288 copied bytes, 14 promoted, 1072 promoted bytes, 68136816 freed). Preserve the window-level evidence grade; do not read this as a single-commit bisect." + }, + "e18b24c420c7c2f389607537618699ffc6aa9d1d": { + "pull_request": 8900, + "category": "measured: weak shape-transition target edges", + "evidence": "Fresh parent d02892491 / e18b24c42 builds show 10 copied_objects 8487 -> 7457 and copied_bytes 529144 -> 327328, with lower 13/14 retention. The target keys array is rewritten but no longer retained solely by a transition-cache entry; dead entries are pruned. This explains part, not all, of the Aug-28 window." + }, + "d923b8dcf08f2afed71777c33423362411039cf0": { + "pull_request": 9373, + "category": "measured: short-concat memo changes large-Eden occupancy", + "evidence": "Fresh parent 3fc54c441 / d923b8dcf builds show 13 copied_objects 0 -> 356717, copied_bytes 0 -> 20594360, promoted_objects 531314 -> 365297, heap_total_bytes 58720256 -> 61865984. Reused row tags change nursery occupancy and the collection cohort under the existing policy. #9359 was a false lead: its changes are behind PERRY_GC_VERIFY_MARK. Subsequent values differ; this A/B establishes the crossing, not a claim that every final byte was introduced here." + }, + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7": { + "pull_request": 9628, + "category": "bracketed: Sep-3 liveness and retention repair", + "evidence": "Within ed7019ea8..218da7e44, the CI row set changes at the same count (42): 09 gains copied_objects/copied_bytes while 05 and 12 heap_used breaches and 14 copied_bytes clear. #9628/#9629 removes false retention from two GC passes. Window attribution, not an isolated A/B. Final classify runs show zero conservative-scan excess for all probes." + }, + "5196d830fd0689937db94598d94662d531d4ee92": { + "pull_request": 9706, + "category": "bracketed: Sep-4 shape storage and array packing", + "evidence": "Within 28c292517..ef79a0211, shape descriptors move to an id-indexed slab (#9706), uncarried descriptors are pruned (#9726), and arena right-sizing/forwarding-stub fixes land (#9717). 07 heap_used rises to 1072456 while copied_objects falls to 4991; 07 packing/promotion-order sensitivity is documented by #9790. The accepted +87616-byte release tail is real, not a conservative-stack artifact. This is a reviewed window-level tradeoff, not proof of one unique causal commit. https://github.com/PerryTS/perry/issues/9829#issuecomment-5555805084" + }, + "22c07ac5b44c5ff6a988257067110c7451da0223": { + "pull_request": 9833, + "category": "measured: included receiver-store workload repair", + "evidence": "Probe change from open PR #9833 is included here (2400000 iterations). Seven-repeat main A/B gives minor_cycles 0 -> 9, copied_objects 0 -> 18660, freed_bytes 0 -> 88858544, and heap_used 464072 -> 244648. The protected fixed probe passes; a non-allocating RHS restores the all-zero collection signature. This dependency is included in this PR, not claimed already merged." + } + }, + "cells": [ + { + "probe": "01_nursery_churn", + "metric": "copied_objects", + "previous_median": 6354.0, + "accepted_median": 4977.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "01_nursery_churn", + "metric": "copied_bytes", + "previous_median": 430008.0, + "accepted_median": 234536.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "01_nursery_churn", + "metric": "freed_bytes", + "previous_median": 13201144.0, + "accepted_median": 11299648.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "02_survivor_promotion", + "metric": "copied_bytes", + "previous_median": 1780080.0, + "accepted_median": 1450992.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140" + ] + }, + { + "probe": "03_cross_gen_writes", + "metric": "freed_bytes", + "previous_median": 11304368.0, + "accepted_median": 9240008.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140" + ] + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "copied_objects", + "previous_median": 450.0, + "accepted_median": 3622.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b" + ] + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "copied_bytes", + "previous_median": 25832.0, + "accepted_median": 149400.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b" + ] + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "promoted_objects", + "previous_median": 10.0, + "accepted_median": 682.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b" + ] + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "freed_bytes", + "previous_median": 83880432.0, + "accepted_median": 73346416.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b" + ] + }, + { + "probe": "05_closure_capture", + "metric": "copied_objects", + "previous_median": 5272.0, + "accepted_median": 20079.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "05_closure_capture", + "metric": "copied_bytes", + "previous_median": 233384.0, + "accepted_median": 743384.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "05_closure_capture", + "metric": "promoted_objects", + "previous_median": 0.0, + "accepted_median": 3390.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "05_closure_capture", + "metric": "promoted_bytes", + "previous_median": 0.0, + "accepted_median": 122120.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "05_closure_capture", + "metric": "freed_bytes", + "previous_median": 32422176.0, + "accepted_median": 26862320.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "06_string_retention", + "metric": "copied_objects", + "previous_median": 11138.0, + "accepted_median": 298.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "06_string_retention", + "metric": "copied_bytes", + "previous_median": 682240.0, + "accepted_median": 72288.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "06_string_retention", + "metric": "promoted_objects", + "previous_median": 4960.0, + "accepted_median": 14.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "06_string_retention", + "metric": "promoted_bytes", + "previous_median": 248512.0, + "accepted_median": 1072.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "06_string_retention", + "metric": "freed_bytes", + "previous_median": 75244808.0, + "accepted_median": 68136816.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "07_array_grow_evacuate", + "metric": "heap_used_bytes", + "previous_median": 984840.0, + "accepted_median": 1072456.0, + "causes": [ + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "5196d830fd0689937db94598d94662d531d4ee92" + ] + }, + { + "probe": "07_array_grow_evacuate", + "metric": "heap_total_bytes", + "previous_median": 23068672.0, + "accepted_median": 25165824.0, + "causes": [ + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "5196d830fd0689937db94598d94662d531d4ee92" + ] + }, + { + "probe": "07_array_grow_evacuate", + "metric": "copied_objects", + "previous_median": 6387.0, + "accepted_median": 4991.0, + "causes": [ + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "5196d830fd0689937db94598d94662d531d4ee92" + ] + }, + { + "probe": "08_map_set_sidetables", + "metric": "copied_objects", + "previous_median": 3017.0, + "accepted_median": 2489.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140" + ] + }, + { + "probe": "09_try_catch_roots", + "metric": "copied_objects", + "previous_median": 0.0, + "accepted_median": 4962.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "d923b8dcf08f2afed71777c33423362411039cf0", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "09_try_catch_roots", + "metric": "copied_bytes", + "previous_median": 0.0, + "accepted_median": 230352.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "d923b8dcf08f2afed71777c33423362411039cf0", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "09_try_catch_roots", + "metric": "promoted_objects", + "previous_median": 6240.0, + "accepted_median": 0.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "d923b8dcf08f2afed71777c33423362411039cf0", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "09_try_catch_roots", + "metric": "promoted_bytes", + "previous_median": 421800.0, + "accepted_median": 0.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "d923b8dcf08f2afed71777c33423362411039cf0", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "heap_total_bytes", + "previous_median": 12582912.0, + "accepted_median": 16777216.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "minor_cycles", + "previous_median": 1.0, + "accepted_median": 9.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "step_cycles", + "previous_median": 1.0, + "accepted_median": 9.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "copied_objects", + "previous_median": 8160.0, + "accepted_median": 18660.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "copied_bytes", + "previous_median": 506200.0, + "accepted_median": 825520.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "promoted_objects", + "previous_median": 0.0, + "accepted_median": 4722.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "promoted_bytes", + "previous_median": 0.0, + "accepted_median": 228440.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "freed_bytes", + "previous_median": 8930928.0, + "accepted_median": 88858544.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "11_collect_at_depth", + "metric": "copied_objects", + "previous_median": 6245.0, + "accepted_median": 4975.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "d923b8dcf08f2afed71777c33423362411039cf0" + ] + }, + { + "probe": "11_collect_at_depth", + "metric": "copied_bytes", + "previous_median": 422744.0, + "accepted_median": 230872.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "d923b8dcf08f2afed71777c33423362411039cf0" + ] + }, + { + "probe": "12_large_live_set", + "metric": "copied_bytes", + "previous_median": 2851312.0, + "accepted_median": 2327008.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140" + ] + }, + { + "probe": "12_large_live_set", + "metric": "promoted_bytes", + "previous_median": 25395472.0, + "accepted_median": 21201208.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140" + ] + }, + { + "probe": "12_large_live_set", + "metric": "freed_bytes", + "previous_median": 76315456.0, + "accepted_median": 63732784.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140" + ] + }, + { + "probe": "13_large_eden_survivors", + "metric": "copied_objects", + "previous_median": 0.0, + "accepted_median": 377569.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "e18b24c420c7c2f389607537618699ffc6aa9d1d", + "d923b8dcf08f2afed71777c33423362411039cf0" + ] + }, + { + "probe": "13_large_eden_survivors", + "metric": "copied_bytes", + "previous_median": 0.0, + "accepted_median": 21429712.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "e18b24c420c7c2f389607537618699ffc6aa9d1d", + "d923b8dcf08f2afed71777c33423362411039cf0" + ] + }, + { + "probe": "13_large_eden_survivors", + "metric": "promoted_objects", + "previous_median": 541614.0, + "accepted_median": 386271.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "e18b24c420c7c2f389607537618699ffc6aa9d1d", + "d923b8dcf08f2afed71777c33423362411039cf0" + ] + }, + { + "probe": "13_large_eden_survivors", + "metric": "promoted_bytes", + "previous_median": 30316904.0, + "accepted_median": 21777792.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "e18b24c420c7c2f389607537618699ffc6aa9d1d", + "d923b8dcf08f2afed71777c33423362411039cf0" + ] + }, + { + "probe": "13_large_eden_survivors", + "metric": "freed_bytes", + "previous_median": 97086288.0, + "accepted_median": 108243696.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "e18b24c420c7c2f389607537618699ffc6aa9d1d", + "d923b8dcf08f2afed71777c33423362411039cf0" + ] + }, + { + "probe": "14_grow_then_churn", + "metric": "copied_objects", + "previous_median": 307.0, + "accepted_median": 330.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "e18b24c420c7c2f389607537618699ffc6aa9d1d" + ] + } + ] } } diff --git a/benchmarks/gc_ratchet/evidence/9829-recovery.json b/benchmarks/gc_ratchet/evidence/9829-recovery.json new file mode 100644 index 0000000000..e2e5aae85d --- /dev/null +++ b/benchmarks/gc_ratchet/evidence/9829-recovery.json @@ -0,0 +1,4931 @@ +{ + "issue": 9829, + "source_commit": "3e3e652e007ac853bf934daa86fa36c870b0e3e2", + "compiler_commit": "d36a1af0c205ebdc7cf7f75b351ea34b5bc0fc0b", + "code_tree": "ea7b395f07b51662a4c94ae5a16e61a5f7b5aeaa", + "build": { + "command": "cargo build --release -p perry -p perry-runtime-static -p perry-stdlib-static", + "env": { + "CARGO_PROFILE_RELEASE_CODEGEN_UNITS": "16", + "CARGO_PROFILE_RELEASE_LTO": "false", + "PERRY_NO_AUTO_OPTIMIZE": "1" + }, + "binaries": { + "perry": { + "size": 117268864, + "sha256": "6fde7dcbb9be714705cc0ebe84e5ab8a0549a1b0e12796fc294c7e872fe32cf0" + }, + "libperry_runtime.a": { + "size": 108253612, + "sha256": "9abf7b9fa1e94f84e6bd1ff4f40c662633491d9267482ebd848e7aa32d14c802" + }, + "libperry_stdlib.a": { + "size": 244536102, + "sha256": "b4e9a0f948da340032c83fa958f959071a2f7fc5428cf899acca9019a0dee2ab" + } + } + }, + "main_ci": { + "run_id": 33989581881, + "url": "https://github.com/PerryTS/perry/actions/runs/33989581881", + "measurement_sha256": "c84af230345b664beeb7845420dec58159e58ff5310dc553e2fe7b737f6231a0", + "measurement": { + "generated_at": "2026-09-05T22:14:04+00:00", + "platform": "darwin-arm64", + "run_config": { + "repeats": 7, + "warmup": 1, + "traced_runs": 2, + "probes": [ + "01_nursery_churn", + "02_survivor_promotion", + "03_cross_gen_writes", + "04_dead_after_deep_stack", + "05_closure_capture", + "06_string_retention", + "07_array_grow_evacuate", + "08_map_set_sidetables", + "09_try_catch_roots", + "10_store_receiver_across_alloc", + "11_collect_at_depth", + "12_large_live_set", + "13_large_eden_survivors", + "14_grow_then_churn" + ] + }, + "probes": { + "01_nursery_churn": { + "stdout": "probe:01_nursery_churn\nchecksum:-1399701504\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [236616, 236616, 236616, 236616, 236616, 236616, 236616], + "heap_total_bytes": [14680064, 14680064, 14680064, 14680064, 14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4977, 4977], + "copied_bytes": [234536, 234536], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [11299648, 11299648] + } + }, + "02_survivor_promotion": { + "stdout": "probe:02_survivor_promotion\nchecksum:1679883264\nlive:40000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [2358144, 2358144, 2358144, 2358144, 2358144, 2358144, 2358144], + "heap_total_bytes": [15728640, 15728640, 15728640, 15728640, 15728640, 15728640, 15728640], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [35465, 35465], + "copied_bytes": [1450992, 1450992], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9034672, 9034672] + } + }, + "03_cross_gen_writes": { + "stdout": "probe:03_cross_gen_writes\nchecksum:2034120704\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [528, 528, 528, 528, 528, 528, 528], + "heap_total_bytes": [12582912, 12582912, 12582912, 12582912, 12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4107, 4107], + "copied_bytes": [197056, 197056], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9240008, 9240008] + } + }, + "04_dead_after_deep_stack": { + "stdout": "probe:04_dead_after_deep_stack\nchecksum:9814000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [536, 536, 536, 536, 536, 536, 536], + "heap_total_bytes": [18874368, 18874368, 18874368, 18874368, 18874368, 18874368, 18874368], + "minor_cycles": [7, 7], + "step_cycles": [7, 7], + "copied_objects": [3622, 3622], + "copied_bytes": [149400, 149400], + "promoted_objects": [682, 682], + "promoted_bytes": [27320, 27320], + "freed_bytes": [73346416, 73346416] + } + }, + "05_closure_capture": { + "stdout": "probe:05_closure_capture\nchecksum:399974400\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [544, 544, 544, 544, 544, 544, 544], + "heap_total_bytes": [14680064, 14680064, 14680064, 14680064, 14680064, 14680064, 14680064], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [20079, 20079], + "copied_bytes": [743384, 743384], + "promoted_objects": [3390, 3390], + "promoted_bytes": [122120, 122120], + "freed_bytes": [26862320, 26862320] + } + }, + "06_string_retention": { + "stdout": "probe:06_string_retention\nchecksum:1112804\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1072, 1072, 1072, 1072, 1072, 1072, 1072], + "heap_total_bytes": [22020096, 22020096, 22020096, 22020096, 22020096, 22020096, 22020096], + "minor_cycles": [4, 4], + "step_cycles": [4, 4], + "copied_objects": [298, 298], + "copied_bytes": [72288, 72288], + "promoted_objects": [14, 14], + "promoted_bytes": [1072, 1072], + "freed_bytes": [68136816, 68136816] + } + }, + "07_array_grow_evacuate": { + "stdout": "probe:07_array_grow_evacuate\nchecksum:21891160\nsurvivors:94\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1072456, 1072456, 1072456, 1072456, 1072456, 1072456, 1072456], + "heap_total_bytes": [25165824, 25165824, 25165824, 25165824, 25165824, 25165824, 25165824], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [4991, 4991], + "copied_bytes": [2404720, 2404720], + "promoted_objects": [4769, 4769], + "promoted_bytes": [623120, 623120], + "freed_bytes": [83788528, 83788528] + } + }, + "08_map_set_sidetables": { + "stdout": "probe:08_map_set_sidetables\nchecksum:1109446656\nfinal_sizes:0,0\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [688, 688, 688, 688, 688, 688, 688], + "heap_total_bytes": [14680064, 14680064, 14680064, 14680064, 14680064, 14680064, 14680064], + "minor_cycles": [6, 6], + "step_cycles": [6, 6], + "copied_objects": [2489, 2489], + "copied_bytes": [99656, 99656], + "promoted_objects": [14, 14], + "promoted_bytes": [592, 592], + "freed_bytes": [56598904, 56598904] + } + }, + "09_try_catch_roots": { + "stdout": "probe:09_try_catch_roots\nchecksum:-1465100498\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [247392, 247392, 247392, 247392, 247392, 247392, 247392], + "heap_total_bytes": [14680064, 14680064, 14680064, 14680064, 14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4962, 4962], + "copied_bytes": [230352, 230352], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [10255312, 10255312] + } + }, + "10_store_receiver_across_alloc": { + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:204277248\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [464072, 464072, 464072, 464072, 464072, 464072, 464072], + "heap_total_bytes": [10485760, 10485760, 10485760, 10485760, 10485760, 10485760, 10485760], + "minor_cycles": [0, 0], + "step_cycles": [0, 0], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [0, 0] + } + }, + "11_collect_at_depth": { + "stdout": "probe:11_collect_at_depth\nchecksum:-1569838524\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [247712, 247712, 247712, 247712, 247712, 247712, 247712], + "heap_total_bytes": [12582912, 12582912, 12582912, 12582912, 12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4975, 4975], + "copied_bytes": [230872, 230872], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9206240, 9206240] + } + }, + "12_large_live_set": { + "stdout": "probe:12_large_live_set\nchecksum:1678627152\nwalked:700000\nsum:186514128\nkept:10938\nkeptSum:-466842304\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [569448, 569448, 569448, 569448, 569448, 569448, 569448], + "heap_total_bytes": [72351744, 72351744, 72351744, 72351744, 72351744, 72351744, 72351744], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [58175, 58175], + "copied_bytes": [2327008, 2327008], + "promoted_objects": [530030, 530030], + "promoted_bytes": [21201208, 21201208], + "freed_bytes": [63732784, 63732784] + } + }, + "13_large_eden_survivors": { + "stdout": "probe:13_large_eden_survivors\nchecksum:1846988256\nhits:3000000\ntagChars:1722368\npeerIds:-131072\nnotes:11719\nnoteSum:22499072\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "64" + }, + "samples": { + "heap_used_bytes": [227048, 227048, 227048, 227048, 227048, 227048, 227048], + "heap_total_bytes": [61865984, 61865984, 61865984, 61865984, 61865984, 61865984, 63963136], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [377569, 377569], + "copied_bytes": [21429712, 21429712], + "promoted_objects": [386271, 386271], + "promoted_bytes": [21777792, 21777792], + "freed_bytes": [108243696, 108243696] + } + }, + "14_grow_then_churn": { + "stdout": "probe:14_grow_then_churn\ncacheSum:2050177040\nchurnSum:815300000\nliveSum:2008997952\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "1", + "PERRY_GC_MAJOR_PACING_FLOOR_MB": "1" + }, + "samples": { + "heap_used_bytes": [430584, 430584, 430584, 430584, 430584, 430584, 430584], + "heap_total_bytes": [23068672, 23068672, 23068672, 23068672, 23068672, 23068672, 23068672], + "minor_cycles": [22, 22], + "step_cycles": [23, 23], + "copied_objects": [330, 330], + "copied_bytes": [1277072, 1277072], + "promoted_objects": [406707, 406707], + "promoted_bytes": [147967960, 147967960], + "freed_bytes": [119606864, 119606864] + } + } + } + } + }, + "main_before": { + "generated_at": "2026-09-06T03:44:09+00:00", + "platform": "linux-x86_64", + "run_config": { + "repeats": 7, + "warmup": 1, + "traced_runs": 2, + "probes": [ + "01_nursery_churn", + "02_survivor_promotion", + "03_cross_gen_writes", + "04_dead_after_deep_stack", + "05_closure_capture", + "06_string_retention", + "07_array_grow_evacuate", + "08_map_set_sidetables", + "09_try_catch_roots", + "10_store_receiver_across_alloc", + "11_collect_at_depth", + "12_large_live_set", + "13_large_eden_survivors", + "14_grow_then_churn" + ] + }, + "probes": { + "01_nursery_churn": { + "stdout": "probe:01_nursery_churn\nchecksum:-1399701504\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [236616, 236616, 236616, 236616, 236616, 236616, 236616], + "heap_total_bytes": [14680064, 14680064, 14680064, 14680064, 14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4977, 4977], + "copied_bytes": [234536, 234536], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [11299648, 11299648] + } + }, + "02_survivor_promotion": { + "stdout": "probe:02_survivor_promotion\nchecksum:1679883264\nlive:40000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [2358144, 2358144, 2358144, 2358144, 2358144, 2358144, 2358144], + "heap_total_bytes": [15728640, 15728640, 15728640, 15728640, 15728640, 15728640, 15728640], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [35465, 35465], + "copied_bytes": [1450992, 1450992], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9034672, 9034672] + } + }, + "03_cross_gen_writes": { + "stdout": "probe:03_cross_gen_writes\nchecksum:2034120704\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [528, 528, 528, 528, 528, 528, 528], + "heap_total_bytes": [12582912, 12582912, 12582912, 12582912, 12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4107, 4107], + "copied_bytes": [197056, 197056], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9240008, 9240008] + } + }, + "04_dead_after_deep_stack": { + "stdout": "probe:04_dead_after_deep_stack\nchecksum:9814000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [536, 536, 536, 536, 536, 536, 536], + "heap_total_bytes": [18874368, 18874368, 18874368, 18874368, 18874368, 18874368, 18874368], + "minor_cycles": [7, 7], + "step_cycles": [7, 7], + "copied_objects": [3622, 3622], + "copied_bytes": [149400, 149400], + "promoted_objects": [682, 682], + "promoted_bytes": [27320, 27320], + "freed_bytes": [73346416, 73346416] + } + }, + "05_closure_capture": { + "stdout": "probe:05_closure_capture\nchecksum:399974400\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [544, 544, 544, 544, 544, 544, 544], + "heap_total_bytes": [14680064, 14680064, 14680064, 14680064, 14680064, 14680064, 14680064], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [20079, 20079], + "copied_bytes": [743384, 743384], + "promoted_objects": [3390, 3390], + "promoted_bytes": [122120, 122120], + "freed_bytes": [26862320, 26862320] + } + }, + "06_string_retention": { + "stdout": "probe:06_string_retention\nchecksum:1112804\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1072, 1072, 1072, 1072, 1072, 1072, 1072], + "heap_total_bytes": [22020096, 22020096, 22020096, 22020096, 22020096, 22020096, 22020096], + "minor_cycles": [4, 4], + "step_cycles": [4, 4], + "copied_objects": [298, 298], + "copied_bytes": [72288, 72288], + "promoted_objects": [14, 14], + "promoted_bytes": [1072, 1072], + "freed_bytes": [68136816, 68136816] + } + }, + "07_array_grow_evacuate": { + "stdout": "probe:07_array_grow_evacuate\nchecksum:21891160\nsurvivors:94\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1072456, 1072456, 1072456, 1072456, 1072456, 1072456, 1072456], + "heap_total_bytes": [25165824, 25165824, 25165824, 25165824, 25165824, 25165824, 25165824], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [4991, 4991], + "copied_bytes": [2404720, 2404720], + "promoted_objects": [4769, 4769], + "promoted_bytes": [623120, 623120], + "freed_bytes": [83788528, 83788528] + } + }, + "08_map_set_sidetables": { + "stdout": "probe:08_map_set_sidetables\nchecksum:1109446656\nfinal_sizes:0,0\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [688, 688, 688, 688, 688, 688, 688], + "heap_total_bytes": [14680064, 14680064, 14680064, 14680064, 14680064, 14680064, 14680064], + "minor_cycles": [6, 6], + "step_cycles": [6, 6], + "copied_objects": [2489, 2489], + "copied_bytes": [99656, 99656], + "promoted_objects": [14, 14], + "promoted_bytes": [592, 592], + "freed_bytes": [56598904, 56598904] + } + }, + "09_try_catch_roots": { + "stdout": "probe:09_try_catch_roots\nchecksum:-1465100498\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [247392, 247392, 247392, 247392, 247392, 247392, 247392], + "heap_total_bytes": [14680064, 14680064, 14680064, 14680064, 14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4962, 4962], + "copied_bytes": [230352, 230352], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [10255312, 10255312] + } + }, + "10_store_receiver_across_alloc": { + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:204277248\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [464072, 464072, 464072, 464072, 464072, 464072, 464072], + "heap_total_bytes": [10485760, 10485760, 10485760, 10485760, 10485760, 10485760, 10485760], + "minor_cycles": [0, 0], + "step_cycles": [0, 0], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [0, 0] + } + }, + "11_collect_at_depth": { + "stdout": "probe:11_collect_at_depth\nchecksum:-1569838524\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [247712, 247712, 247712, 247712, 247712, 247712, 247712], + "heap_total_bytes": [12582912, 12582912, 12582912, 12582912, 12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4975, 4975], + "copied_bytes": [230872, 230872], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9206240, 9206240] + } + }, + "12_large_live_set": { + "stdout": "probe:12_large_live_set\nchecksum:1678627152\nwalked:700000\nsum:186514128\nkept:10938\nkeptSum:-466842304\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [569448, 569448, 569448, 569448, 569448, 569448, 569448], + "heap_total_bytes": [72351744, 72351744, 72351744, 72351744, 72351744, 72351744, 72351744], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [58175, 58175], + "copied_bytes": [2327008, 2327008], + "promoted_objects": [530030, 530030], + "promoted_bytes": [21201208, 21201208], + "freed_bytes": [63732784, 63732784] + } + }, + "13_large_eden_survivors": { + "stdout": "probe:13_large_eden_survivors\nchecksum:1846988256\nhits:3000000\ntagChars:1722368\npeerIds:-131072\nnotes:11719\nnoteSum:22499072\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "64" + }, + "samples": { + "heap_used_bytes": [227048, 227048, 227048, 227048, 227048, 227048, 227048], + "heap_total_bytes": [61865984, 61865984, 61865984, 61865984, 61865984, 61865984, 61865984], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [377569, 377569], + "copied_bytes": [21429712, 21429712], + "promoted_objects": [386271, 386271], + "promoted_bytes": [21777792, 21777792], + "freed_bytes": [108243696, 108243696] + } + }, + "14_grow_then_churn": { + "stdout": "probe:14_grow_then_churn\ncacheSum:2050177040\nchurnSum:815300000\nliveSum:2008997952\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "1", + "PERRY_GC_MAJOR_PACING_FLOOR_MB": "1" + }, + "samples": { + "heap_used_bytes": [430584, 430584, 430584, 430584, 430584, 430584, 430584], + "heap_total_bytes": [23068672, 23068672, 23068672, 23068672, 23068672, 23068672, 23068672], + "minor_cycles": [22, 22], + "step_cycles": [23, 23], + "copied_objects": [330, 330], + "copied_bytes": [1277072, 1277072], + "promoted_objects": [406707, 406707], + "promoted_bytes": [147967960, 147967960], + "freed_bytes": [119606864, 119606864] + } + } + } + }, + "main_with_repaired_probe": { + "generated_at": "2026-09-06T03:44:52+00:00", + "platform": "linux-x86_64", + "run_config": { + "repeats": 7, + "warmup": 1, + "traced_runs": 2, + "probes": [ + "01_nursery_churn", + "02_survivor_promotion", + "03_cross_gen_writes", + "04_dead_after_deep_stack", + "05_closure_capture", + "06_string_retention", + "07_array_grow_evacuate", + "08_map_set_sidetables", + "09_try_catch_roots", + "10_store_receiver_across_alloc", + "11_collect_at_depth", + "12_large_live_set", + "13_large_eden_survivors", + "14_grow_then_churn" + ] + }, + "probes": { + "01_nursery_churn": { + "stdout": "probe:01_nursery_churn\nchecksum:-1399701504\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [236616, 236616, 236616, 236616, 236616, 236616, 236616], + "heap_total_bytes": [14680064, 14680064, 14680064, 14680064, 14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4977, 4977], + "copied_bytes": [234536, 234536], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [11299648, 11299648] + } + }, + "02_survivor_promotion": { + "stdout": "probe:02_survivor_promotion\nchecksum:1679883264\nlive:40000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [2358144, 2358144, 2358144, 2358144, 2358144, 2358144, 2358144], + "heap_total_bytes": [15728640, 15728640, 15728640, 15728640, 15728640, 15728640, 15728640], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [35465, 35465], + "copied_bytes": [1450992, 1450992], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9034672, 9034672] + } + }, + "03_cross_gen_writes": { + "stdout": "probe:03_cross_gen_writes\nchecksum:2034120704\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [528, 528, 528, 528, 528, 528, 528], + "heap_total_bytes": [12582912, 12582912, 12582912, 12582912, 12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4107, 4107], + "copied_bytes": [197056, 197056], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9240008, 9240008] + } + }, + "04_dead_after_deep_stack": { + "stdout": "probe:04_dead_after_deep_stack\nchecksum:9814000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [536, 536, 536, 536, 536, 536, 536], + "heap_total_bytes": [18874368, 18874368, 18874368, 18874368, 18874368, 18874368, 18874368], + "minor_cycles": [7, 7], + "step_cycles": [7, 7], + "copied_objects": [3622, 3622], + "copied_bytes": [149400, 149400], + "promoted_objects": [682, 682], + "promoted_bytes": [27320, 27320], + "freed_bytes": [73346416, 73346416] + } + }, + "05_closure_capture": { + "stdout": "probe:05_closure_capture\nchecksum:399974400\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [544, 544, 544, 544, 544, 544, 544], + "heap_total_bytes": [14680064, 14680064, 14680064, 14680064, 14680064, 14680064, 14680064], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [20079, 20079], + "copied_bytes": [743384, 743384], + "promoted_objects": [3390, 3390], + "promoted_bytes": [122120, 122120], + "freed_bytes": [26862320, 26862320] + } + }, + "06_string_retention": { + "stdout": "probe:06_string_retention\nchecksum:1112804\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1072, 1072, 1072, 1072, 1072, 1072, 1072], + "heap_total_bytes": [22020096, 22020096, 22020096, 22020096, 22020096, 22020096, 22020096], + "minor_cycles": [4, 4], + "step_cycles": [4, 4], + "copied_objects": [298, 298], + "copied_bytes": [72288, 72288], + "promoted_objects": [14, 14], + "promoted_bytes": [1072, 1072], + "freed_bytes": [68136816, 68136816] + } + }, + "07_array_grow_evacuate": { + "stdout": "probe:07_array_grow_evacuate\nchecksum:21891160\nsurvivors:94\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1072456, 1072456, 1072456, 1072456, 1072456, 1072456, 1072456], + "heap_total_bytes": [25165824, 25165824, 25165824, 25165824, 25165824, 25165824, 25165824], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [4991, 4991], + "copied_bytes": [2404720, 2404720], + "promoted_objects": [4769, 4769], + "promoted_bytes": [623120, 623120], + "freed_bytes": [83788528, 83788528] + } + }, + "08_map_set_sidetables": { + "stdout": "probe:08_map_set_sidetables\nchecksum:1109446656\nfinal_sizes:0,0\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [688, 688, 688, 688, 688, 688, 688], + "heap_total_bytes": [14680064, 14680064, 14680064, 14680064, 14680064, 14680064, 14680064], + "minor_cycles": [6, 6], + "step_cycles": [6, 6], + "copied_objects": [2489, 2489], + "copied_bytes": [99656, 99656], + "promoted_objects": [14, 14], + "promoted_bytes": [592, 592], + "freed_bytes": [56598904, 56598904] + } + }, + "09_try_catch_roots": { + "stdout": "probe:09_try_catch_roots\nchecksum:-1465100498\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [247392, 247392, 247392, 247392, 247392, 247392, 247392], + "heap_total_bytes": [14680064, 14680064, 14680064, 14680064, 14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4962, 4962], + "copied_bytes": [230352, 230352], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [10255312, 10255312] + } + }, + "10_store_receiver_across_alloc": { + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:-1837890048\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [244648, 244648, 244648, 244648, 244648, 244648, 244648], + "heap_total_bytes": [16777216, 16777216, 16777216, 16777216, 16777216, 16777216, 16777216], + "minor_cycles": [9, 9], + "step_cycles": [9, 9], + "copied_objects": [18660, 18660], + "copied_bytes": [825520, 825520], + "promoted_objects": [4722, 4722], + "promoted_bytes": [228440, 228440], + "freed_bytes": [88858544, 88858544] + } + }, + "11_collect_at_depth": { + "stdout": "probe:11_collect_at_depth\nchecksum:-1569838524\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [247712, 247712, 247712, 247712, 247712, 247712, 247712], + "heap_total_bytes": [12582912, 12582912, 12582912, 12582912, 12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4975, 4975], + "copied_bytes": [230872, 230872], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9206240, 9206240] + } + }, + "12_large_live_set": { + "stdout": "probe:12_large_live_set\nchecksum:1678627152\nwalked:700000\nsum:186514128\nkept:10938\nkeptSum:-466842304\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [569448, 569448, 569448, 569448, 569448, 569448, 569448], + "heap_total_bytes": [72351744, 72351744, 72351744, 72351744, 72351744, 72351744, 72351744], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [58175, 58175], + "copied_bytes": [2327008, 2327008], + "promoted_objects": [530030, 530030], + "promoted_bytes": [21201208, 21201208], + "freed_bytes": [63732784, 63732784] + } + }, + "13_large_eden_survivors": { + "stdout": "probe:13_large_eden_survivors\nchecksum:1846988256\nhits:3000000\ntagChars:1722368\npeerIds:-131072\nnotes:11719\nnoteSum:22499072\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "64" + }, + "samples": { + "heap_used_bytes": [227048, 227048, 227048, 227048, 227048, 227048, 227048], + "heap_total_bytes": [61865984, 61865984, 61865984, 61865984, 61865984, 63963136, 61865984], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [377569, 377569], + "copied_bytes": [21429712, 21429712], + "promoted_objects": [386271, 386271], + "promoted_bytes": [21777792, 21777792], + "freed_bytes": [108243696, 108243696] + } + }, + "14_grow_then_churn": { + "stdout": "probe:14_grow_then_churn\ncacheSum:2050177040\nchurnSum:815300000\nliveSum:2008997952\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "1", + "PERRY_GC_MAJOR_PACING_FLOOR_MB": "1" + }, + "samples": { + "heap_used_bytes": [430584, 430584, 430584, 430584, 430584, 430584, 430584], + "heap_total_bytes": [23068672, 23068672, 23068672, 23068672, 23068672, 23068672, 23068672], + "minor_cycles": [22, 22], + "step_cycles": [23, 23], + "copied_objects": [330, 330], + "copied_bytes": [1277072, 1277072], + "promoted_objects": [406707, 406707], + "promoted_bytes": [147967960, 147967960], + "freed_bytes": [119606864, 119606864] + } + } + } + }, + "historical_builds": { + "066f9d5953f4dabbe2a792b6026944e256ac32e0": { + "code_tree": "fb6ba697b0fde68a41485f6920cbd4acea98cba8", + "measurement_sha256": "f8992a9901aa6a78659850bb06a3eb726e56b621aab549d01b957935e33648d7", + "measurement": { + "generated_at": "2026-09-06T03:57:33+00:00", + "platform": "linux-x86_64", + "run_config": { + "repeats": 3, + "warmup": 1, + "traced_runs": 2, + "probes": [ + "01_nursery_churn", + "02_survivor_promotion", + "03_cross_gen_writes", + "04_dead_after_deep_stack", + "05_closure_capture", + "06_string_retention", + "07_array_grow_evacuate", + "08_map_set_sidetables", + "09_try_catch_roots", + "10_store_receiver_across_alloc", + "11_collect_at_depth", + "12_large_live_set", + "13_large_eden_survivors", + "14_grow_then_churn" + ] + }, + "probes": { + "01_nursery_churn": { + "stdout": "probe:01_nursery_churn\nchecksum:-1399701504\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [225680, 225680, 225680], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [6650, 6650], + "copied_bytes": [448432, 448432], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [11085768, 11085768] + } + }, + "02_survivor_promotion": { + "stdout": "probe:02_survivor_promotion\nchecksum:1679883264\nlive:40000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [2349952, 2349952, 2349952], + "heap_total_bytes": [15728640, 15728640, 15728640], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [37171, 37171], + "copied_bytes": [1667008, 1667008], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8818632, 8818632] + } + }, + "03_cross_gen_writes": { + "stdout": "probe:03_cross_gen_writes\nchecksum:2034120704\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [528, 528, 528], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4107, 4107], + "copied_bytes": [197056, 197056], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9240008, 9240008] + } + }, + "04_dead_after_deep_stack": { + "stdout": "probe:04_dead_after_deep_stack\nchecksum:9814000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [27416, 27416, 27416], + "heap_total_bytes": [18874368, 18874368, 18874368], + "minor_cycles": [7, 7], + "step_cycles": [7, 7], + "copied_objects": [3622, 3622], + "copied_bytes": [149400, 149400], + "promoted_objects": [682, 682], + "promoted_bytes": [27320, 27320], + "freed_bytes": [73346416, 73346416] + } + }, + "05_closure_capture": { + "stdout": "probe:05_closure_capture\nchecksum:399974400\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [252112, 252112, 252112], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [20335, 20335], + "copied_bytes": [752600, 752600], + "promoted_objects": [3518, 3518], + "promoted_bytes": [126728, 126728], + "freed_bytes": [26857712, 26857712] + } + }, + "06_string_retention": { + "stdout": "probe:06_string_retention\nchecksum:1112804\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [226152, 226152, 226152], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [11691, 11691], + "copied_bytes": [720120, 720120], + "promoted_objects": [5210, 5210], + "promoted_bytes": [262224, 262224], + "freed_bytes": [74180784, 74180784] + } + }, + "07_array_grow_evacuate": { + "stdout": "probe:07_array_grow_evacuate\nchecksum:21891160\nsurvivors:94\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [998352, 998352, 998352], + "heap_total_bytes": [25165824, 25165824, 25165824], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [6671, 6671], + "copied_bytes": [2620776, 2620776], + "promoted_objects": [5128, 5128], + "promoted_bytes": [628168, 628168], + "freed_bytes": [83783560, 83783560] + } + }, + "08_map_set_sidetables": { + "stdout": "probe:08_map_set_sidetables\nchecksum:1109446656\nfinal_sizes:0,0\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [672, 672, 672], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [6, 6], + "step_cycles": [6, 6], + "copied_objects": [3388, 3388], + "copied_bytes": [135448, 135448], + "promoted_objects": [15, 15], + "promoted_bytes": [576, 576], + "freed_bytes": [56606720, 56606720] + } + }, + "09_try_catch_roots": { + "stdout": "probe:09_try_catch_roots\nchecksum:-1465100498\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [225680, 225680, 225680], + "heap_total_bytes": [13631488, 13631488, 13631488], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [6524, 6524], + "promoted_bytes": [438784, 438784], + "freed_bytes": [10046896, 10046896] + } + }, + "10_store_receiver_across_alloc": { + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:204277248\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [233896, 233896, 233896], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [8452, 8452], + "copied_bytes": [516712, 516712], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8920520, 8920520] + } + }, + "11_collect_at_depth": { + "stdout": "probe:11_collect_at_depth\nchecksum:-1569838524\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [225680, 225680, 225680], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [6532, 6532], + "copied_bytes": [441384, 441384], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8995680, 8995680] + } + }, + "12_large_live_set": { + "stdout": "probe:12_large_live_set\nchecksum:1678627152\nwalked:700000\nsum:186514128\nkept:10938\nkeptSum:-466842304\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1681712, 1681712, 1681712], + "heap_total_bytes": [72351744, 72351744, 72351744], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [58175, 58175], + "copied_bytes": [2327008, 2327008], + "promoted_objects": [530030, 530030], + "promoted_bytes": [21201208, 21201208], + "freed_bytes": [63732664, 63732664] + } + }, + "13_large_eden_survivors": { + "stdout": "probe:13_large_eden_survivors\nchecksum:1846988256\nhits:3000000\ntagChars:1722368\npeerIds:-131072\nnotes:11719\nnoteSum:22499072\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "64" + }, + "samples": { + "heap_used_bytes": [465848, 465848, 465848], + "heap_total_bytes": [56623104, 56623104, 56623104], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [542500, 542500], + "promoted_bytes": [28173424, 28173424], + "freed_bytes": [86649800, 86649800] + } + }, + "14_grow_then_churn": { + "stdout": "probe:14_grow_then_churn\ncacheSum:2050177040\nchurnSum:815300000\nliveSum:2008997952\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "1", + "PERRY_GC_MAJOR_PACING_FLOOR_MB": "1" + }, + "samples": { + "heap_used_bytes": [415392, 415392, 415392], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [22, 22], + "step_cycles": [23, 23], + "copied_objects": [330, 330], + "copied_bytes": [1277072, 1277072], + "promoted_objects": [406708, 406708], + "promoted_bytes": [147967856, 147967856], + "freed_bytes": [119598704, 119598704] + } + } + } + }, + "build_log_sha256": "ea511f11de8bc93afe9be43d8a9e30d9afed00c42f937a7ec6c23fd3c8c163ee" + }, + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b": { + "code_tree": "7c0231b405413312de85c8e5428e1171267d0150", + "measurement_sha256": "64830ae82cf5923726255d2654c7c27d3c419006028e48b43f5d13793dd1ff4d", + "measurement": { + "generated_at": "2026-09-06T03:50:41+00:00", + "platform": "linux-x86_64", + "run_config": { + "repeats": 3, + "warmup": 1, + "traced_runs": 2, + "probes": [ + "01_nursery_churn", + "02_survivor_promotion", + "03_cross_gen_writes", + "04_dead_after_deep_stack", + "05_closure_capture", + "06_string_retention", + "07_array_grow_evacuate", + "08_map_set_sidetables", + "09_try_catch_roots", + "10_store_receiver_across_alloc", + "11_collect_at_depth", + "12_large_live_set", + "13_large_eden_survivors", + "14_grow_then_churn" + ] + }, + "probes": { + "01_nursery_churn": { + "stdout": "probe:01_nursery_churn\nchecksum:-1399701504\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [225680, 225680, 225680], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [6650, 6650], + "copied_bytes": [448432, 448432], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [11085768, 11085768] + } + }, + "02_survivor_promotion": { + "stdout": "probe:02_survivor_promotion\nchecksum:1679883264\nlive:40000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [2349952, 2349952, 2349952], + "heap_total_bytes": [15728640, 15728640, 15728640], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [37171, 37171], + "copied_bytes": [1667008, 1667008], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8818632, 8818632] + } + }, + "03_cross_gen_writes": { + "stdout": "probe:03_cross_gen_writes\nchecksum:2034120704\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [528, 528, 528], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4107, 4107], + "copied_bytes": [197056, 197056], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9240008, 9240008] + } + }, + "04_dead_after_deep_stack": { + "stdout": "probe:04_dead_after_deep_stack\nchecksum:9814000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [27416, 27416, 27416], + "heap_total_bytes": [18874368, 18874368, 18874368], + "minor_cycles": [7, 7], + "step_cycles": [7, 7], + "copied_objects": [3622, 3622], + "copied_bytes": [149400, 149400], + "promoted_objects": [682, 682], + "promoted_bytes": [27320, 27320], + "freed_bytes": [73346416, 73346416] + } + }, + "05_closure_capture": { + "stdout": "probe:05_closure_capture\nchecksum:399974400\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [252112, 252112, 252112], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [20335, 20335], + "copied_bytes": [752600, 752600], + "promoted_objects": [3518, 3518], + "promoted_bytes": [126728, 126728], + "freed_bytes": [26857712, 26857712] + } + }, + "06_string_retention": { + "stdout": "probe:06_string_retention\nchecksum:1112804\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [226152, 226152, 226152], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [10, 10], + "step_cycles": [10, 10], + "copied_objects": [12141, 12141], + "copied_bytes": [1005128, 1005128], + "promoted_objects": [5082, 5082], + "promoted_bytes": [225840, 225840], + "freed_bytes": [160153064, 160153064] + } + }, + "07_array_grow_evacuate": { + "stdout": "probe:07_array_grow_evacuate\nchecksum:21891160\nsurvivors:94\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [998352, 998352, 998352], + "heap_total_bytes": [25165824, 25165824, 25165824], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [6672, 6672], + "copied_bytes": [2620920, 2620920], + "promoted_objects": [5128, 5128], + "promoted_bytes": [628168, 628168], + "freed_bytes": [83783560, 83783560] + } + }, + "08_map_set_sidetables": { + "stdout": "probe:08_map_set_sidetables\nchecksum:1109446656\nfinal_sizes:0,0\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [672, 672, 672], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [6, 6], + "step_cycles": [6, 6], + "copied_objects": [3388, 3388], + "copied_bytes": [135448, 135448], + "promoted_objects": [15, 15], + "promoted_bytes": [576, 576], + "freed_bytes": [56606720, 56606720] + } + }, + "09_try_catch_roots": { + "stdout": "probe:09_try_catch_roots\nchecksum:-1465100498\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [225680, 225680, 225680], + "heap_total_bytes": [13631488, 13631488, 13631488], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [6524, 6524], + "promoted_bytes": [438784, 438784], + "freed_bytes": [10046896, 10046896] + } + }, + "10_store_receiver_across_alloc": { + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:204277248\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [233896, 233896, 233896], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [8455, 8455], + "copied_bytes": [517144, 517144], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8920088, 8920088] + } + }, + "11_collect_at_depth": { + "stdout": "probe:11_collect_at_depth\nchecksum:-1569838524\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [225680, 225680, 225680], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [6531, 6531], + "copied_bytes": [441240, 441240], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8995824, 8995824] + } + }, + "12_large_live_set": { + "stdout": "probe:12_large_live_set\nchecksum:1678627152\nwalked:700000\nsum:186514128\nkept:10938\nkeptSum:-466842304\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1681712, 1681712, 1681712], + "heap_total_bytes": [72351744, 72351744, 72351744], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [58175, 58175], + "copied_bytes": [2327008, 2327008], + "promoted_objects": [530030, 530030], + "promoted_bytes": [21201208, 21201208], + "freed_bytes": [63732664, 63732664] + } + }, + "13_large_eden_survivors": { + "stdout": "probe:13_large_eden_survivors\nchecksum:1846988256\nhits:3000000\ntagChars:1722368\npeerIds:-131072\nnotes:11719\nnoteSum:22499072\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "64" + }, + "samples": { + "heap_used_bytes": [465704, 465704, 465704], + "heap_total_bytes": [56623104, 56623104, 56623104], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [542499, 542499], + "promoted_bytes": [28173280, 28173280], + "freed_bytes": [86649800, 86649800] + } + }, + "14_grow_then_churn": { + "stdout": "probe:14_grow_then_churn\ncacheSum:2050177040\nchurnSum:815300000\nliveSum:2008997952\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "1", + "PERRY_GC_MAJOR_PACING_FLOOR_MB": "1" + }, + "samples": { + "heap_used_bytes": [412648, 412648, 412648], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [22, 22], + "step_cycles": [23, 23], + "copied_objects": [330, 330], + "copied_bytes": [1277072, 1277072], + "promoted_objects": [406708, 406708], + "promoted_bytes": [147967856, 147967856], + "freed_bytes": [119598704, 119598704] + } + } + } + }, + "build_log_sha256": "c0dcf19af925a494a7177c9e5b793857fa62b64784cdd36a81866610174fd74d" + }, + "3fc54c44144ac6697b521c36d406055d3573e715": { + "code_tree": "abb8f937405cabba842f15b7a05b1c23a9c1f6ce", + "measurement_sha256": "99b597921ca3844c5609fcc482593ed35c33c22fde1823ccfacab3e2db0ec8a4", + "measurement": { + "generated_at": "2026-09-06T03:52:23+00:00", + "platform": "linux-x86_64", + "run_config": { + "repeats": 3, + "warmup": 1, + "traced_runs": 2, + "probes": [ + "01_nursery_churn", + "02_survivor_promotion", + "03_cross_gen_writes", + "04_dead_after_deep_stack", + "05_closure_capture", + "06_string_retention", + "07_array_grow_evacuate", + "08_map_set_sidetables", + "09_try_catch_roots", + "10_store_receiver_across_alloc", + "11_collect_at_depth", + "12_large_live_set", + "13_large_eden_survivors", + "14_grow_then_churn" + ] + }, + "probes": { + "01_nursery_churn": { + "stdout": "probe:01_nursery_churn\nchecksum:-1399701504\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [233096, 233096, 233096], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [5664, 5664], + "copied_bytes": [260744, 260744], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [11273440, 11273440] + } + }, + "02_survivor_promotion": { + "stdout": "probe:02_survivor_promotion\nchecksum:1679883264\nlive:40000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [2357368, 2357368, 2357368], + "heap_total_bytes": [15728640, 15728640, 15728640], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [36152, 36152], + "copied_bytes": [1477200, 1477200], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9008464, 9008464] + } + }, + "03_cross_gen_writes": { + "stdout": "probe:03_cross_gen_writes\nchecksum:2034120704\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [528, 528, 528], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4107, 4107], + "copied_bytes": [197056, 197056], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9240008, 9240008] + } + }, + "04_dead_after_deep_stack": { + "stdout": "probe:04_dead_after_deep_stack\nchecksum:9814000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [27416, 27416, 27416], + "heap_total_bytes": [18874368, 18874368, 18874368], + "minor_cycles": [7, 7], + "step_cycles": [7, 7], + "copied_objects": [3622, 3622], + "copied_bytes": [149400, 149400], + "promoted_objects": [682, 682], + "promoted_bytes": [27320, 27320], + "freed_bytes": [73346416, 73346416] + } + }, + "05_closure_capture": { + "stdout": "probe:05_closure_capture\nchecksum:399974400\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [247504, 247504, 247504], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [20079, 20079], + "copied_bytes": [743384, 743384], + "promoted_objects": [3390, 3390], + "promoted_bytes": [122120, 122120], + "freed_bytes": [26862320, 26862320] + } + }, + "06_string_retention": { + "stdout": "probe:06_string_retention\nchecksum:1112804\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1072, 1072, 1072], + "heap_total_bytes": [22020096, 22020096, 22020096], + "minor_cycles": [4, 4], + "step_cycles": [4, 4], + "copied_objects": [298, 298], + "copied_bytes": [72288, 72288], + "promoted_objects": [14, 14], + "promoted_bytes": [1072, 1072], + "freed_bytes": [68136816, 68136816] + } + }, + "07_array_grow_evacuate": { + "stdout": "probe:07_array_grow_evacuate\nchecksum:21891160\nsurvivors:94\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1005768, 1005768, 1005768], + "heap_total_bytes": [25165824, 25165824, 25165824], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [5678, 5678], + "copied_bytes": [2430928, 2430928], + "promoted_objects": [5130, 5130], + "promoted_bytes": [635584, 635584], + "freed_bytes": [83776064, 83776064] + } + }, + "08_map_set_sidetables": { + "stdout": "probe:08_map_set_sidetables\nchecksum:1109446656\nfinal_sizes:0,0\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [688, 688, 688], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [6, 6], + "step_cycles": [6, 6], + "copied_objects": [2489, 2489], + "copied_bytes": [99656, 99656], + "promoted_objects": [14, 14], + "promoted_bytes": [592, 592], + "freed_bytes": [56598904, 56598904] + } + }, + "09_try_catch_roots": { + "stdout": "probe:09_try_catch_roots\nchecksum:-1465100498\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [233096, 233096, 233096], + "heap_total_bytes": [13631488, 13631488, 13631488], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [5633, 5633], + "promoted_bytes": [255920, 255920], + "freed_bytes": [10229744, 10229744] + } + }, + "10_store_receiver_across_alloc": { + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:204277248\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [255056, 255056, 255056], + "heap_total_bytes": [10485760, 10485760, 10485760], + "minor_cycles": [0, 0], + "step_cycles": [0, 0], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [0, 0] + } + }, + "11_collect_at_depth": { + "stdout": "probe:11_collect_at_depth\nchecksum:-1569838524\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [233096, 233096, 233096], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [5537, 5537], + "copied_bytes": [252080, 252080], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9185032, 9185032] + } + }, + "12_large_live_set": { + "stdout": "probe:12_large_live_set\nchecksum:1678627152\nwalked:700000\nsum:186514128\nkept:10938\nkeptSum:-466842304\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1681552, 1681552, 1681552], + "heap_total_bytes": [72351744, 72351744, 72351744], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [58175, 58175], + "copied_bytes": [2327008, 2327008], + "promoted_objects": [530030, 530030], + "promoted_bytes": [21201208, 21201208], + "freed_bytes": [63732784, 63732784] + } + }, + "13_large_eden_survivors": { + "stdout": "probe:13_large_eden_survivors\nchecksum:1846988256\nhits:3000000\ntagChars:1722368\npeerIds:-131072\nnotes:11719\nnoteSum:22499072\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "64" + }, + "samples": { + "heap_used_bytes": [289992, 289992, 289992], + "heap_total_bytes": [58720256, 58720256, 58720256], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [531314, 531314], + "promoted_bytes": [27578240, 27578240], + "freed_bytes": [86649800, 86649800] + } + }, + "14_grow_then_churn": { + "stdout": "probe:14_grow_then_churn\ncacheSum:2050177040\nchurnSum:815300000\nliveSum:2008997952\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "1", + "PERRY_GC_MAJOR_PACING_FLOOR_MB": "1" + }, + "samples": { + "heap_used_bytes": [233344, 233344, 233344], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [22, 22], + "step_cycles": [23, 23], + "copied_objects": [325, 325], + "copied_bytes": [1276352, 1276352], + "promoted_objects": [406707, 406707], + "promoted_bytes": [147967960, 147967960], + "freed_bytes": [118559056, 118559056] + } + } + } + }, + "build_log_sha256": "1b75d54beb25623449556a1c693d87c30c3b7581e1e816075a28db89cc257bf8" + }, + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a": { + "code_tree": "2eeef7d284d3dc5a4f96b2947c706b637a6dedfb", + "measurement_sha256": "aac277a71373776c3d237509fa091a3aa1dcc26276a2b6ea9f6c7d55206dc72d", + "measurement": { + "generated_at": "2026-09-06T03:59:06+00:00", + "platform": "linux-x86_64", + "run_config": { + "repeats": 3, + "warmup": 1, + "traced_runs": 2, + "probes": [ + "01_nursery_churn", + "02_survivor_promotion", + "03_cross_gen_writes", + "04_dead_after_deep_stack", + "05_closure_capture", + "06_string_retention", + "07_array_grow_evacuate", + "08_map_set_sidetables", + "09_try_catch_roots", + "10_store_receiver_across_alloc", + "11_collect_at_depth", + "12_large_live_set", + "13_large_eden_survivors", + "14_grow_then_churn" + ] + }, + "probes": { + "01_nursery_churn": { + "stdout": "probe:01_nursery_churn\nchecksum:-1399701504\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [225760, 225760, 225760], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [6684, 6684], + "copied_bytes": [453304, 453304], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [11080896, 11080896] + } + }, + "02_survivor_promotion": { + "stdout": "probe:02_survivor_promotion\nchecksum:1679883264\nlive:40000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [2350032, 2350032, 2350032], + "heap_total_bytes": [15728640, 15728640, 15728640], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [37199, 37199], + "copied_bytes": [1669872, 1669872], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8815776, 8815776] + } + }, + "03_cross_gen_writes": { + "stdout": "probe:03_cross_gen_writes\nchecksum:2034120704\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [528, 528, 528], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4107, 4107], + "copied_bytes": [197056, 197056], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9240008, 9240008] + } + }, + "04_dead_after_deep_stack": { + "stdout": "probe:04_dead_after_deep_stack\nchecksum:9814000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [27416, 27416, 27416], + "heap_total_bytes": [18874368, 18874368, 18874368], + "minor_cycles": [7, 7], + "step_cycles": [7, 7], + "copied_objects": [3622, 3622], + "copied_bytes": [149400, 149400], + "promoted_objects": [682, 682], + "promoted_bytes": [27320, 27320], + "freed_bytes": [73346416, 73346416] + } + }, + "05_closure_capture": { + "stdout": "probe:05_closure_capture\nchecksum:399974400\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [252112, 252112, 252112], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [20335, 20335], + "copied_bytes": [752600, 752600], + "promoted_objects": [3518, 3518], + "promoted_bytes": [126728, 126728], + "freed_bytes": [26857712, 26857712] + } + }, + "06_string_retention": { + "stdout": "probe:06_string_retention\nchecksum:1112804\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [226232, 226232, 226232], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [11714, 11714], + "copied_bytes": [721632, 721632], + "promoted_objects": [5212, 5212], + "promoted_bytes": [262304, 262304], + "freed_bytes": [74180992, 74180992] + } + }, + "07_array_grow_evacuate": { + "stdout": "probe:07_array_grow_evacuate\nchecksum:21891160\nsurvivors:94\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [998432, 998432, 998432], + "heap_total_bytes": [25165824, 25165824, 25165824], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [6695, 6695], + "copied_bytes": [2622016, 2622016], + "promoted_objects": [5130, 5130], + "promoted_bytes": [628248, 628248], + "freed_bytes": [83783416, 83783416] + } + }, + "08_map_set_sidetables": { + "stdout": "probe:08_map_set_sidetables\nchecksum:1109446656\nfinal_sizes:0,0\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [672, 672, 672], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [6, 6], + "step_cycles": [6, 6], + "copied_objects": [3388, 3388], + "copied_bytes": [135448, 135448], + "promoted_objects": [15, 15], + "promoted_bytes": [576, 576], + "freed_bytes": [56606720, 56606720] + } + }, + "09_try_catch_roots": { + "stdout": "probe:09_try_catch_roots\nchecksum:-1465100498\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [225760, 225760, 225760], + "heap_total_bytes": [13631488, 13631488, 13631488], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [6560, 6560], + "promoted_bytes": [445288, 445288], + "freed_bytes": [10040360, 10040360] + } + }, + "10_store_receiver_across_alloc": { + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:204277248\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [233976, 233976, 233976], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [8494, 8494], + "copied_bytes": [523992, 523992], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8913240, 8913240] + } + }, + "11_collect_at_depth": { + "stdout": "probe:11_collect_at_depth\nchecksum:-1569838524\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [225760, 225760, 225760], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [6562, 6562], + "copied_bytes": [445328, 445328], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8991784, 8991784] + } + }, + "12_large_live_set": { + "stdout": "probe:12_large_live_set\nchecksum:1678627152\nwalked:700000\nsum:186514128\nkept:10938\nkeptSum:-466842304\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1681712, 1681712, 1681712], + "heap_total_bytes": [72351744, 72351744, 72351744], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [58175, 58175], + "copied_bytes": [2327008, 2327008], + "promoted_objects": [530030, 530030], + "promoted_bytes": [21201208, 21201208], + "freed_bytes": [63732664, 63732664] + } + }, + "13_large_eden_survivors": { + "stdout": "probe:13_large_eden_survivors\nchecksum:1846988256\nhits:3000000\ntagChars:1722368\npeerIds:-131072\nnotes:11719\nnoteSum:22499072\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "64" + }, + "samples": { + "heap_used_bytes": [476088, 476088, 476088], + "heap_total_bytes": [56623104, 56623104, 56623104], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [542533, 542533], + "promoted_bytes": [28178696, 28178696], + "freed_bytes": [86649800, 86649800] + } + }, + "14_grow_then_churn": { + "stdout": "probe:14_grow_then_churn\ncacheSum:2050177040\nchurnSum:815300000\nliveSum:2008997952\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "1", + "PERRY_GC_MAJOR_PACING_FLOOR_MB": "1" + }, + "samples": { + "heap_used_bytes": [419064, 419064, 419064], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [22, 22], + "step_cycles": [23, 23], + "copied_objects": [330, 330], + "copied_bytes": [1277072, 1277072], + "promoted_objects": [406708, 406708], + "promoted_bytes": [147967856, 147967856], + "freed_bytes": [119615024, 119615024] + } + } + } + }, + "build_log_sha256": "6c5e83e54c22f5b22e7a5b0b3e60e78633692825803c8819ff6fd0f3277fc82f" + }, + "c2da03439e6e848cfed73163c131acb601ae96dc": { + "code_tree": "c201770f043f56ae8a91ba8c17c0ab676c6208ae", + "measurement_sha256": "de233972f6b8aa83e39d991cf237ecf47a4e26b4750f3f3ee6618720b4348049", + "measurement": { + "generated_at": "2026-09-06T03:49:06+00:00", + "platform": "linux-x86_64", + "run_config": { + "repeats": 3, + "warmup": 1, + "traced_runs": 2, + "probes": [ + "01_nursery_churn", + "02_survivor_promotion", + "03_cross_gen_writes", + "04_dead_after_deep_stack", + "05_closure_capture", + "06_string_retention", + "07_array_grow_evacuate", + "08_map_set_sidetables", + "09_try_catch_roots", + "10_store_receiver_across_alloc", + "11_collect_at_depth", + "12_large_live_set", + "13_large_eden_survivors", + "14_grow_then_churn" + ] + }, + "probes": { + "01_nursery_churn": { + "stdout": "probe:01_nursery_churn\nchecksum:-1399701504\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [225344, 225344, 225344], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [6632, 6632], + "copied_bytes": [445800, 445800], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [11088408, 11088408] + } + }, + "02_survivor_promotion": { + "stdout": "probe:02_survivor_promotion\nchecksum:1679883264\nlive:40000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [2349616, 2349616, 2349616], + "heap_total_bytes": [15728640, 15728640, 15728640], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [37161, 37161], + "copied_bytes": [1664616, 1664616], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8821032, 8821032] + } + }, + "03_cross_gen_writes": { + "stdout": "probe:03_cross_gen_writes\nchecksum:2034120704\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [528, 528, 528], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4107, 4107], + "copied_bytes": [197056, 197056], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9240008, 9240008] + } + }, + "04_dead_after_deep_stack": { + "stdout": "probe:04_dead_after_deep_stack\nchecksum:9814000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [536, 536, 536], + "heap_total_bytes": [18874368, 18874368, 18874368], + "minor_cycles": [6, 6], + "step_cycles": [6, 6], + "copied_objects": [528, 528], + "copied_bytes": [27096, 27096], + "promoted_objects": [10, 10], + "promoted_bytes": [440, 440], + "freed_bytes": [76532640, 76532640] + } + }, + "05_closure_capture": { + "stdout": "probe:05_closure_capture\nchecksum:399974400\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [5152, 5152, 5152], + "heap_total_bytes": [13631488, 13631488, 13631488], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [5648, 5648], + "copied_bytes": [225976, 225976], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [29269088, 29269088] + } + }, + "06_string_retention": { + "stdout": "probe:06_string_retention\nchecksum:1112804\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [225816, 225816, 225816], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [10, 10], + "step_cycles": [10, 10], + "copied_objects": [12116, 12116], + "copied_bytes": [986808, 986808], + "promoted_objects": [5075, 5075], + "promoted_bytes": [225504, 225504], + "freed_bytes": [158064256, 158064256] + } + }, + "07_array_grow_evacuate": { + "stdout": "probe:07_array_grow_evacuate\nchecksum:21891160\nsurvivors:94\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [998016, 998016, 998016], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [6670, 6670], + "copied_bytes": [2694488, 2694488], + "promoted_objects": [5122, 5122], + "promoted_bytes": [628872, 628872], + "freed_bytes": [83708400, 83708400] + } + }, + "08_map_set_sidetables": { + "stdout": "probe:08_map_set_sidetables\nchecksum:1109446656\nfinal_sizes:0,0\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [672, 672, 672], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [6, 6], + "step_cycles": [6, 6], + "copied_objects": [3388, 3388], + "copied_bytes": [135448, 135448], + "promoted_objects": [15, 15], + "promoted_bytes": [576, 576], + "freed_bytes": [56606720, 56606720] + } + }, + "09_try_catch_roots": { + "stdout": "probe:09_try_catch_roots\nchecksum:-1465100498\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [225344, 225344, 225344], + "heap_total_bytes": [13631488, 13631488, 13631488], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [6513, 6513], + "promoted_bytes": [438632, 438632], + "freed_bytes": [10047056, 10047056] + } + }, + "10_store_receiver_across_alloc": { + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:204277248\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [233560, 233560, 233560], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [8440, 8440], + "copied_bytes": [515824, 515824], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8921344, 8921344] + } + }, + "11_collect_at_depth": { + "stdout": "probe:11_collect_at_depth\nchecksum:-1569838524\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [225344, 225344, 225344], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [6527, 6527], + "copied_bytes": [442016, 442016], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8995056, 8995056] + } + }, + "12_large_live_set": { + "stdout": "probe:12_large_live_set\nchecksum:1678627152\nwalked:700000\nsum:186514128\nkept:10938\nkeptSum:-466842304\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1681712, 1681712, 1681712], + "heap_total_bytes": [72351744, 72351744, 72351744], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [58175, 58175], + "copied_bytes": [2327008, 2327008], + "promoted_objects": [530030, 530030], + "promoted_bytes": [21201208, 21201208], + "freed_bytes": [63732664, 63732664] + } + }, + "13_large_eden_survivors": { + "stdout": "probe:13_large_eden_survivors\nchecksum:1846988256\nhits:3000000\ntagChars:1722368\npeerIds:-131072\nnotes:11719\nnoteSum:22499072\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "64" + }, + "samples": { + "heap_used_bytes": [468760, 468760, 468760], + "heap_total_bytes": [56623104, 56623104, 56623104], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [542494, 542494], + "promoted_bytes": [28174112, 28174112], + "freed_bytes": [86649800, 86649800] + } + }, + "14_grow_then_churn": { + "stdout": "probe:14_grow_then_churn\ncacheSum:2050177040\nchurnSum:815300000\nliveSum:2008997952\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "1", + "PERRY_GC_MAJOR_PACING_FLOOR_MB": "1" + }, + "samples": { + "heap_used_bytes": [412224, 412224, 412224], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [22, 22], + "step_cycles": [23, 23], + "copied_objects": [340, 340], + "copied_bytes": [1357872, 1357872], + "promoted_objects": [406062, 406062], + "promoted_bytes": [147510664, 147510664], + "freed_bytes": [119662160, 119662160] + } + } + } + }, + "build_log_sha256": "07cf6e14735b3b6a42c3489815ad57d8435f991709f95490ccbf07799fa110cc" + }, + "d02892491563a9f34d5bc78867764fcad0b07a58": { + "code_tree": "33e62b3c476f0436663c7412d83a2b94d482361a", + "measurement_sha256": "f991086c46123c4ddb078c530e5116dc7854369defd677507f0466cf7b006be6", + "measurement": { + "generated_at": "2026-09-06T04:01:11+00:00", + "platform": "linux-x86_64", + "run_config": { + "repeats": 3, + "warmup": 1, + "traced_runs": 2, + "probes": [ + "01_nursery_churn", + "02_survivor_promotion", + "03_cross_gen_writes", + "04_dead_after_deep_stack", + "05_closure_capture", + "06_string_retention", + "07_array_grow_evacuate", + "08_map_set_sidetables", + "09_try_catch_roots", + "10_store_receiver_across_alloc", + "11_collect_at_depth", + "12_large_live_set", + "13_large_eden_survivors", + "14_grow_then_churn" + ] + }, + "probes": { + "01_nursery_churn": { + "stdout": "probe:01_nursery_churn\nchecksum:-1399701504\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [600, 600, 600], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [268, 268], + "copied_bytes": [14816, 14816], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [11519392, 11519392] + } + }, + "02_survivor_promotion": { + "stdout": "probe:02_survivor_promotion\nchecksum:1679883264\nlive:40000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [2124872, 2124872, 2124872], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [29198, 29198], + "copied_bytes": [1168952, 1168952], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8268128, 8268128] + } + }, + "03_cross_gen_writes": { + "stdout": "probe:03_cross_gen_writes\nchecksum:2034120704\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [528, 528, 528], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4107, 4107], + "copied_bytes": [197056, 197056], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9240008, 9240008] + } + }, + "04_dead_after_deep_stack": { + "stdout": "probe:04_dead_after_deep_stack\nchecksum:9814000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [27416, 27416, 27416], + "heap_total_bytes": [18874368, 18874368, 18874368], + "minor_cycles": [7, 7], + "step_cycles": [7, 7], + "copied_objects": [3622, 3622], + "copied_bytes": [149400, 149400], + "promoted_objects": [682, 682], + "promoted_bytes": [27320, 27320], + "freed_bytes": [73346416, 73346416] + } + }, + "05_closure_capture": { + "stdout": "probe:05_closure_capture\nchecksum:399974400\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [252112, 252112, 252112], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [20335, 20335], + "copied_bytes": [752600, 752600], + "promoted_objects": [3518, 3518], + "promoted_bytes": [126728, 126728], + "freed_bytes": [26857712, 26857712] + } + }, + "06_string_retention": { + "stdout": "probe:06_string_retention\nchecksum:1112804\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1072, 1072, 1072], + "heap_total_bytes": [22020096, 22020096, 22020096], + "minor_cycles": [4, 4], + "step_cycles": [4, 4], + "copied_objects": [298, 298], + "copied_bytes": [72288, 72288], + "promoted_objects": [14, 14], + "promoted_bytes": [1072, 1072], + "freed_bytes": [68136816, 68136816] + } + }, + "07_array_grow_evacuate": { + "stdout": "probe:07_array_grow_evacuate\nchecksum:21891160\nsurvivors:94\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [773272, 773272, 773272], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [328, 328], + "copied_bytes": [2473488, 2473488], + "promoted_objects": [60, 60], + "promoted_bytes": [403400, 403400], + "freed_bytes": [84008304, 84008304] + } + }, + "08_map_set_sidetables": { + "stdout": "probe:08_map_set_sidetables\nchecksum:1109446656\nfinal_sizes:0,0\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [696, 696, 696], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [6, 6], + "step_cycles": [6, 6], + "copied_objects": [3382, 3382], + "copied_bytes": [135280, 135280], + "promoted_objects": [15, 15], + "promoted_bytes": [600, 600], + "freed_bytes": [56606720, 56606720] + } + }, + "09_try_catch_roots": { + "stdout": "probe:09_try_catch_roots\nchecksum:-1465100498\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [600, 600, 600], + "heap_total_bytes": [13631488, 13631488, 13631488], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [141, 141], + "promoted_bytes": [6152, 6152], + "freed_bytes": [10479488, 10479488] + } + }, + "10_store_receiver_across_alloc": { + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:204277248\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [240264, 240264, 240264], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [8487, 8487], + "copied_bytes": [529144, 529144], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8908040, 8908040] + } + }, + "11_collect_at_depth": { + "stdout": "probe:11_collect_at_depth\nchecksum:-1569838524\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [600, 600, 600], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [141, 141], + "copied_bytes": [6152, 6152], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9430952, 9430952] + } + }, + "12_large_live_set": { + "stdout": "probe:12_large_live_set\nchecksum:1678627152\nwalked:700000\nsum:186514128\nkept:10938\nkeptSum:-466842304\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1681552, 1681552, 1681552], + "heap_total_bytes": [72351744, 72351744, 72351744], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [58175, 58175], + "copied_bytes": [2327008, 2327008], + "promoted_objects": [530030, 530030], + "promoted_bytes": [21201208, 21201208], + "freed_bytes": [63732784, 63732784] + } + }, + "13_large_eden_survivors": { + "stdout": "probe:13_large_eden_survivors\nchecksum:1846988256\nhits:3000000\ntagChars:1722368\npeerIds:-131072\nnotes:11719\nnoteSum:22499072\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "64" + }, + "samples": { + "heap_used_bytes": [485992, 485992, 485992], + "heap_total_bytes": [56623104, 56623104, 56623104], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [542546, 542546], + "promoted_bytes": [28187176, 28187176], + "freed_bytes": [86649800, 86649800] + } + }, + "14_grow_then_churn": { + "stdout": "probe:14_grow_then_churn\ncacheSum:2050177040\nchurnSum:815300000\nliveSum:2008997952\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "1", + "PERRY_GC_MAJOR_PACING_FLOOR_MB": "1" + }, + "samples": { + "heap_used_bytes": [425168, 425168, 425168], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [22, 22], + "step_cycles": [23, 23], + "copied_objects": [330, 330], + "copied_bytes": [1277072, 1277072], + "promoted_objects": [406707, 406707], + "promoted_bytes": [147967904, 147967904], + "freed_bytes": [119606864, 119606864] + } + } + } + }, + "build_log_sha256": "01cea0e1b00c21fdb0622caa3403ae6c9183051581f1947475ff512edea3e4af" + }, + "d923b8dcf08f2afed71777c33423362411039cf0": { + "code_tree": "754b855d450836a70c10c21a6bcfe885fd63cf84", + "measurement_sha256": "4ebc31dfd52915755dca716e0ebf32598ce0a57b0409b0b705576c9b5473f170", + "measurement": { + "generated_at": "2026-09-06T03:53:51+00:00", + "platform": "linux-x86_64", + "run_config": { + "repeats": 3, + "warmup": 1, + "traced_runs": 2, + "probes": [ + "01_nursery_churn", + "02_survivor_promotion", + "03_cross_gen_writes", + "04_dead_after_deep_stack", + "05_closure_capture", + "06_string_retention", + "07_array_grow_evacuate", + "08_map_set_sidetables", + "09_try_catch_roots", + "10_store_receiver_across_alloc", + "11_collect_at_depth", + "12_large_live_set", + "13_large_eden_survivors", + "14_grow_then_churn" + ] + }, + "probes": { + "01_nursery_churn": { + "stdout": "probe:01_nursery_churn\nchecksum:-1399701504\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [233096, 233096, 233096], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [5664, 5664], + "copied_bytes": [260744, 260744], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [11273440, 11273440] + } + }, + "02_survivor_promotion": { + "stdout": "probe:02_survivor_promotion\nchecksum:1679883264\nlive:40000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [2357368, 2357368, 2357368], + "heap_total_bytes": [15728640, 15728640, 15728640], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [36152, 36152], + "copied_bytes": [1477200, 1477200], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9008464, 9008464] + } + }, + "03_cross_gen_writes": { + "stdout": "probe:03_cross_gen_writes\nchecksum:2034120704\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [528, 528, 528], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4107, 4107], + "copied_bytes": [197056, 197056], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9240008, 9240008] + } + }, + "04_dead_after_deep_stack": { + "stdout": "probe:04_dead_after_deep_stack\nchecksum:9814000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [27416, 27416, 27416], + "heap_total_bytes": [18874368, 18874368, 18874368], + "minor_cycles": [7, 7], + "step_cycles": [7, 7], + "copied_objects": [3622, 3622], + "copied_bytes": [149400, 149400], + "promoted_objects": [682, 682], + "promoted_bytes": [27320, 27320], + "freed_bytes": [73346416, 73346416] + } + }, + "05_closure_capture": { + "stdout": "probe:05_closure_capture\nchecksum:399974400\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [247504, 247504, 247504], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [20079, 20079], + "copied_bytes": [743384, 743384], + "promoted_objects": [3390, 3390], + "promoted_bytes": [122120, 122120], + "freed_bytes": [26862320, 26862320] + } + }, + "06_string_retention": { + "stdout": "probe:06_string_retention\nchecksum:1112804\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1072, 1072, 1072], + "heap_total_bytes": [22020096, 22020096, 22020096], + "minor_cycles": [4, 4], + "step_cycles": [4, 4], + "copied_objects": [298, 298], + "copied_bytes": [72288, 72288], + "promoted_objects": [14, 14], + "promoted_bytes": [1072, 1072], + "freed_bytes": [68136816, 68136816] + } + }, + "07_array_grow_evacuate": { + "stdout": "probe:07_array_grow_evacuate\nchecksum:21891160\nsurvivors:94\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1005768, 1005768, 1005768], + "heap_total_bytes": [25165824, 25165824, 25165824], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [5678, 5678], + "copied_bytes": [2430928, 2430928], + "promoted_objects": [5130, 5130], + "promoted_bytes": [635584, 635584], + "freed_bytes": [83776064, 83776064] + } + }, + "08_map_set_sidetables": { + "stdout": "probe:08_map_set_sidetables\nchecksum:1109446656\nfinal_sizes:0,0\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [688, 688, 688], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [6, 6], + "step_cycles": [6, 6], + "copied_objects": [2489, 2489], + "copied_bytes": [99656, 99656], + "promoted_objects": [14, 14], + "promoted_bytes": [592, 592], + "freed_bytes": [56598904, 56598904] + } + }, + "09_try_catch_roots": { + "stdout": "probe:09_try_catch_roots\nchecksum:-1465100498\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1022296, 1022296, 1022296], + "heap_total_bytes": [13631488, 13631488, 13631488], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [6081, 6081], + "promoted_bytes": [273840, 273840], + "freed_bytes": [10211824, 10211824] + } + }, + "10_store_receiver_across_alloc": { + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:204277248\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [255056, 255056, 255056], + "heap_total_bytes": [10485760, 10485760, 10485760], + "minor_cycles": [0, 0], + "step_cycles": [0, 0], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [0, 0] + } + }, + "11_collect_at_depth": { + "stdout": "probe:11_collect_at_depth\nchecksum:-1569838524\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1233896, 1233896, 1233896], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [5985, 5985], + "copied_bytes": [270000, 270000], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9167112, 9167112] + } + }, + "12_large_live_set": { + "stdout": "probe:12_large_live_set\nchecksum:1678627152\nwalked:700000\nsum:186514128\nkept:10938\nkeptSum:-466842304\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1681552, 1681552, 1681552], + "heap_total_bytes": [72351744, 72351744, 72351744], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [58175, 58175], + "copied_bytes": [2327008, 2327008], + "promoted_objects": [530030, 530030], + "promoted_bytes": [21201208, 21201208], + "freed_bytes": [63732784, 63732784] + } + }, + "13_large_eden_survivors": { + "stdout": "probe:13_large_eden_survivors\nchecksum:1846988256\nhits:3000000\ntagChars:1722368\npeerIds:-131072\nnotes:11719\nnoteSum:22499072\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "64" + }, + "samples": { + "heap_used_bytes": [276272, 276272, 276272], + "heap_total_bytes": [61865984, 61865984, 61865984], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [356717, 356717], + "copied_bytes": [20594360, 20594360], + "promoted_objects": [365297, 365297], + "promoted_bytes": [20936856, 20936856], + "freed_bytes": [111181792, 111181792] + } + }, + "14_grow_then_churn": { + "stdout": "probe:14_grow_then_churn\ncacheSum:2050177040\nchurnSum:815300000\nliveSum:2008997952\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "1", + "PERRY_GC_MAJOR_PACING_FLOOR_MB": "1" + }, + "samples": { + "heap_used_bytes": [233344, 233344, 233344], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [22, 22], + "step_cycles": [23, 23], + "copied_objects": [325, 325], + "copied_bytes": [1276352, 1276352], + "promoted_objects": [406707, 406707], + "promoted_bytes": [147967960, 147967960], + "freed_bytes": [118559056, 118559056] + } + } + } + }, + "build_log_sha256": "385211916be549de7231872e19689dde0f7cfd8e32c06c27d35b4052c632c0e7" + }, + "e18b24c420c7c2f389607537618699ffc6aa9d1d": { + "code_tree": "6b1c14e694c40253f47ef8c09744f662f800ada3", + "measurement_sha256": "9656b6f6e30509a26bd5797b94a6701c71c0d2af9a64df9ff5684cb4801332b9", + "measurement": { + "generated_at": "2026-09-06T04:03:11+00:00", + "platform": "linux-x86_64", + "run_config": { + "repeats": 3, + "warmup": 1, + "traced_runs": 2, + "probes": [ + "01_nursery_churn", + "02_survivor_promotion", + "03_cross_gen_writes", + "04_dead_after_deep_stack", + "05_closure_capture", + "06_string_retention", + "07_array_grow_evacuate", + "08_map_set_sidetables", + "09_try_catch_roots", + "10_store_receiver_across_alloc", + "11_collect_at_depth", + "12_large_live_set", + "13_large_eden_survivors", + "14_grow_then_churn" + ] + }, + "probes": { + "01_nursery_churn": { + "stdout": "probe:01_nursery_churn\nchecksum:-1399701504\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [600, 600, 600], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [268, 268], + "copied_bytes": [14816, 14816], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [11519392, 11519392] + } + }, + "02_survivor_promotion": { + "stdout": "probe:02_survivor_promotion\nchecksum:1679883264\nlive:40000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [2124872, 2124872, 2124872], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [29198, 29198], + "copied_bytes": [1168952, 1168952], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [8268128, 8268128] + } + }, + "03_cross_gen_writes": { + "stdout": "probe:03_cross_gen_writes\nchecksum:2034120704\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [528, 528, 528], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4107, 4107], + "copied_bytes": [197056, 197056], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9240008, 9240008] + } + }, + "04_dead_after_deep_stack": { + "stdout": "probe:04_dead_after_deep_stack\nchecksum:9814000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [27416, 27416, 27416], + "heap_total_bytes": [18874368, 18874368, 18874368], + "minor_cycles": [7, 7], + "step_cycles": [7, 7], + "copied_objects": [3622, 3622], + "copied_bytes": [149400, 149400], + "promoted_objects": [682, 682], + "promoted_bytes": [27320, 27320], + "freed_bytes": [73346416, 73346416] + } + }, + "05_closure_capture": { + "stdout": "probe:05_closure_capture\nchecksum:399974400\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [252112, 252112, 252112], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [20335, 20335], + "copied_bytes": [752600, 752600], + "promoted_objects": [3518, 3518], + "promoted_bytes": [126728, 126728], + "freed_bytes": [26857712, 26857712] + } + }, + "06_string_retention": { + "stdout": "probe:06_string_retention\nchecksum:1112804\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1072, 1072, 1072], + "heap_total_bytes": [22020096, 22020096, 22020096], + "minor_cycles": [4, 4], + "step_cycles": [4, 4], + "copied_objects": [298, 298], + "copied_bytes": [72288, 72288], + "promoted_objects": [14, 14], + "promoted_bytes": [1072, 1072], + "freed_bytes": [68136816, 68136816] + } + }, + "07_array_grow_evacuate": { + "stdout": "probe:07_array_grow_evacuate\nchecksum:21891160\nsurvivors:94\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [773272, 773272, 773272], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [328, 328], + "copied_bytes": [2473488, 2473488], + "promoted_objects": [60, 60], + "promoted_bytes": [403400, 403400], + "freed_bytes": [84008304, 84008304] + } + }, + "08_map_set_sidetables": { + "stdout": "probe:08_map_set_sidetables\nchecksum:1109446656\nfinal_sizes:0,0\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [696, 696, 696], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [6, 6], + "step_cycles": [6, 6], + "copied_objects": [3382, 3382], + "copied_bytes": [135280, 135280], + "promoted_objects": [15, 15], + "promoted_bytes": [600, 600], + "freed_bytes": [56606720, 56606720] + } + }, + "09_try_catch_roots": { + "stdout": "probe:09_try_catch_roots\nchecksum:-1465100498\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [600, 600, 600], + "heap_total_bytes": [13631488, 13631488, 13631488], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [141, 141], + "promoted_bytes": [6152, 6152], + "freed_bytes": [10479488, 10479488] + } + }, + "10_store_receiver_across_alloc": { + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:204277248\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [240264, 240264, 240264], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [7457, 7457], + "copied_bytes": [327328, 327328], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9109856, 9109856] + } + }, + "11_collect_at_depth": { + "stdout": "probe:11_collect_at_depth\nchecksum:-1569838524\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [600, 600, 600], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [141, 141], + "copied_bytes": [6152, 6152], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9430952, 9430952] + } + }, + "12_large_live_set": { + "stdout": "probe:12_large_live_set\nchecksum:1678627152\nwalked:700000\nsum:186514128\nkept:10938\nkeptSum:-466842304\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1681552, 1681552, 1681552], + "heap_total_bytes": [72351744, 72351744, 72351744], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [58175, 58175], + "copied_bytes": [2327008, 2327008], + "promoted_objects": [530030, 530030], + "promoted_bytes": [21201208, 21201208], + "freed_bytes": [63732784, 63732784] + } + }, + "13_large_eden_survivors": { + "stdout": "probe:13_large_eden_survivors\nchecksum:1846988256\nhits:3000000\ntagChars:1722368\npeerIds:-131072\nnotes:11719\nnoteSum:22499072\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "64" + }, + "samples": { + "heap_used_bytes": [294616, 294616, 294616], + "heap_total_bytes": [55574528, 55574528, 55574528], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [0, 0], + "copied_bytes": [0, 0], + "promoted_objects": [541412, 541412], + "promoted_bytes": [27981104, 27981104], + "freed_bytes": [85605320, 85605320] + } + }, + "14_grow_then_churn": { + "stdout": "probe:14_grow_then_churn\ncacheSum:2050177040\nchurnSum:815300000\nliveSum:2008997952\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "1", + "PERRY_GC_MAJOR_PACING_FLOOR_MB": "1" + }, + "samples": { + "heap_used_bytes": [232288, 232288, 232288], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [22, 22], + "step_cycles": [23, 23], + "copied_objects": [325, 325], + "copied_bytes": [1276352, 1276352], + "promoted_objects": [406707, 406707], + "promoted_bytes": [147967904, 147967904], + "freed_bytes": [118559056, 118559056] + } + } + } + }, + "build_log_sha256": "7eb0d11883e7054a6497d7f7a48847a97537b63cd21da28435d0c1a7a0d5b9bb" + } + }, + "protected_probe": { + "fixed": { + "returncode": 0, + "counters": { + "minor_cycles": 9, + "step_cycles": 9, + "copied_objects": 18660, + "copied_bytes": 825520, + "promoted_objects": 4722, + "promoted_bytes": 228440, + "freed_bytes": 88858544 + }, + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:-1837890048\n" + }, + "nonallocating_rhs": { + "returncode": 0, + "counters": { + "minor_cycles": 0, + "step_cycles": 0, + "copied_objects": 0, + "copied_bytes": 0, + "promoted_objects": 0, + "promoted_bytes": 0, + "freed_bytes": 0 + }, + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:0\n" + } + }, + "classification": { + "schema_version": 1, + "kind": "gc-ratchet-classification", + "generated_at": "2026-09-06T03:52:11+00:00", + "platform": "linux-x86_64", + "host": { + "platform": "linux-x86_64", + "hostname": "ideal-mastodon", + "system": "Linux", + "release": "6.17.0-23-generic", + "machine": "x86_64", + "cpu_count": 16, + "load_average": { + "1m": 6.66, + "5m": 5.9, + "15m": 3.67 + }, + "memory_bytes": 66468130816 + }, + "run_config": { + "repeats": 3, + "warmup": 1, + "scan_mode_env": "PERRY_CONSERVATIVE_STACK_SCAN" + }, + "probes": [ + { + "probe": "01_nursery_churn", + "run_env": {}, + "heap_used_bytes": 236616, + "heap_used_samples": [236616, 236616, 236616], + "heap_used_spread_bytes": 0, + "heap_used_precise_bytes": 236616, + "false_root_excess_bytes": 0, + "false_root_excess_pct": 0, + "heap_total_bytes": 14680064, + "heap_total_precise_bytes": 14680064, + "scan_fallback_sites": {}, + "automatic_scan_sites": [] + }, + { + "probe": "02_survivor_promotion", + "run_env": {}, + "heap_used_bytes": 2358144, + "heap_used_samples": [2358144, 2358144, 2358144], + "heap_used_spread_bytes": 0, + "heap_used_precise_bytes": 2358144, + "false_root_excess_bytes": 0, + "false_root_excess_pct": 0, + "heap_total_bytes": 15728640, + "heap_total_precise_bytes": 15728640, + "scan_fallback_sites": {}, + "automatic_scan_sites": [] + }, + { + "probe": "03_cross_gen_writes", + "run_env": {}, + "heap_used_bytes": 528, + "heap_used_samples": [528, 528, 528], + "heap_used_spread_bytes": 0, + "heap_used_precise_bytes": 528, + "false_root_excess_bytes": 0, + "false_root_excess_pct": 0, + "heap_total_bytes": 12582912, + "heap_total_precise_bytes": 12582912, + "scan_fallback_sites": {}, + "automatic_scan_sites": [] + }, + { + "probe": "04_dead_after_deep_stack", + "run_env": {}, + "heap_used_bytes": 536, + "heap_used_samples": [536, 536, 536], + "heap_used_spread_bytes": 0, + "heap_used_precise_bytes": 536, + "false_root_excess_bytes": 0, + "false_root_excess_pct": 0, + "heap_total_bytes": 18874368, + "heap_total_precise_bytes": 18874368, + "scan_fallback_sites": {}, + "automatic_scan_sites": [] + }, + { + "probe": "05_closure_capture", + "run_env": {}, + "heap_used_bytes": 544, + "heap_used_samples": [544, 544, 544], + "heap_used_spread_bytes": 0, + "heap_used_precise_bytes": 544, + "false_root_excess_bytes": 0, + "false_root_excess_pct": 0, + "heap_total_bytes": 14680064, + "heap_total_precise_bytes": 14680064, + "scan_fallback_sites": {}, + "automatic_scan_sites": [] + }, + { + "probe": "06_string_retention", + "run_env": {}, + "heap_used_bytes": 1072, + "heap_used_samples": [1072, 1072, 1072], + "heap_used_spread_bytes": 0, + "heap_used_precise_bytes": 1072, + "false_root_excess_bytes": 0, + "false_root_excess_pct": 0, + "heap_total_bytes": 22020096, + "heap_total_precise_bytes": 22020096, + "scan_fallback_sites": {}, + "automatic_scan_sites": [] + }, + { + "probe": "07_array_grow_evacuate", + "run_env": {}, + "heap_used_bytes": 1072456, + "heap_used_samples": [1072456, 1072456, 1072456], + "heap_used_spread_bytes": 0, + "heap_used_precise_bytes": 1072456, + "false_root_excess_bytes": 0, + "false_root_excess_pct": 0, + "heap_total_bytes": 25165824, + "heap_total_precise_bytes": 25165824, + "scan_fallback_sites": {}, + "automatic_scan_sites": [] + }, + { + "probe": "08_map_set_sidetables", + "run_env": {}, + "heap_used_bytes": 688, + "heap_used_samples": [688, 688, 688], + "heap_used_spread_bytes": 0, + "heap_used_precise_bytes": 688, + "false_root_excess_bytes": 0, + "false_root_excess_pct": 0, + "heap_total_bytes": 14680064, + "heap_total_precise_bytes": 14680064, + "scan_fallback_sites": {}, + "automatic_scan_sites": [] + }, + { + "probe": "09_try_catch_roots", + "run_env": {}, + "heap_used_bytes": 247392, + "heap_used_samples": [247392, 247392, 247392], + "heap_used_spread_bytes": 0, + "heap_used_precise_bytes": 247392, + "false_root_excess_bytes": 0, + "false_root_excess_pct": 0, + "heap_total_bytes": 14680064, + "heap_total_precise_bytes": 14680064, + "scan_fallback_sites": {}, + "automatic_scan_sites": [] + }, + { + "probe": "10_store_receiver_across_alloc", + "run_env": {}, + "heap_used_bytes": 244648, + "heap_used_samples": [244648, 244648, 244648], + "heap_used_spread_bytes": 0, + "heap_used_precise_bytes": 244648, + "false_root_excess_bytes": 0, + "false_root_excess_pct": 0, + "heap_total_bytes": 16777216, + "heap_total_precise_bytes": 16777216, + "scan_fallback_sites": {}, + "automatic_scan_sites": [] + }, + { + "probe": "11_collect_at_depth", + "run_env": {}, + "heap_used_bytes": 247712, + "heap_used_samples": [247712, 247712, 247712], + "heap_used_spread_bytes": 0, + "heap_used_precise_bytes": 247712, + "false_root_excess_bytes": 0, + "false_root_excess_pct": 0, + "heap_total_bytes": 12582912, + "heap_total_precise_bytes": 12582912, + "scan_fallback_sites": {}, + "automatic_scan_sites": [] + }, + { + "probe": "12_large_live_set", + "run_env": {}, + "heap_used_bytes": 569448, + "heap_used_samples": [569448, 569448, 569448], + "heap_used_spread_bytes": 0, + "heap_used_precise_bytes": 569448, + "false_root_excess_bytes": 0, + "false_root_excess_pct": 0, + "heap_total_bytes": 72351744, + "heap_total_precise_bytes": 72351744, + "scan_fallback_sites": {}, + "automatic_scan_sites": [] + }, + { + "probe": "13_large_eden_survivors", + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "64" + }, + "heap_used_bytes": 227048, + "heap_used_samples": [227048, 227048, 227048], + "heap_used_spread_bytes": 0, + "heap_used_precise_bytes": 227048, + "false_root_excess_bytes": 0, + "false_root_excess_pct": 0, + "heap_total_bytes": 63963136, + "heap_total_precise_bytes": 61865984, + "scan_fallback_sites": {}, + "automatic_scan_sites": [] + }, + { + "probe": "14_grow_then_churn", + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "1", + "PERRY_GC_MAJOR_PACING_FLOOR_MB": "1" + }, + "heap_used_bytes": 430584, + "heap_used_samples": [430584, 430584, 430584], + "heap_used_spread_bytes": 0, + "heap_used_precise_bytes": 430584, + "false_root_excess_bytes": 0, + "false_root_excess_pct": 0, + "heap_total_bytes": 23068672, + "heap_total_precise_bytes": 23068672, + "scan_fallback_sites": { + "old_reclaim_alloc_point": { + "automatic": true, + "count": 1 + } + }, + "automatic_scan_sites": [ + "old_reclaim_alloc_point" + ] + } + ] + }, + "concat_site_cache_control": { + "env": { + "PERRY_CONCAT_SITE_CACHE": "0" + }, + "measurement": { + "generated_at": "2026-09-06T04:02:46+00:00", + "platform": "linux-x86_64", + "run_config": { + "repeats": 3, + "warmup": 1, + "traced_runs": 2, + "probes": [ + "01_nursery_churn", + "02_survivor_promotion", + "03_cross_gen_writes", + "04_dead_after_deep_stack", + "05_closure_capture", + "06_string_retention", + "07_array_grow_evacuate", + "08_map_set_sidetables", + "09_try_catch_roots", + "10_store_receiver_across_alloc", + "11_collect_at_depth", + "12_large_live_set", + "13_large_eden_survivors", + "14_grow_then_churn" + ] + }, + "probes": { + "01_nursery_churn": { + "stdout": "probe:01_nursery_churn\nchecksum:-1399701504\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [236616, 236616, 236616], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4977, 4977], + "copied_bytes": [234536, 234536], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [11299648, 11299648] + } + }, + "02_survivor_promotion": { + "stdout": "probe:02_survivor_promotion\nchecksum:1679883264\nlive:40000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [2358144, 2358144, 2358144], + "heap_total_bytes": [15728640, 15728640, 15728640], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [35465, 35465], + "copied_bytes": [1450992, 1450992], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9034672, 9034672] + } + }, + "03_cross_gen_writes": { + "stdout": "probe:03_cross_gen_writes\nchecksum:2034120704\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [528, 528, 528], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4107, 4107], + "copied_bytes": [197056, 197056], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9240008, 9240008] + } + }, + "04_dead_after_deep_stack": { + "stdout": "probe:04_dead_after_deep_stack\nchecksum:9814000\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [536, 536, 536], + "heap_total_bytes": [18874368, 18874368, 18874368], + "minor_cycles": [7, 7], + "step_cycles": [7, 7], + "copied_objects": [3622, 3622], + "copied_bytes": [149400, 149400], + "promoted_objects": [682, 682], + "promoted_bytes": [27320, 27320], + "freed_bytes": [73346416, 73346416] + } + }, + "05_closure_capture": { + "stdout": "probe:05_closure_capture\nchecksum:399974400\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [544, 544, 544], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [20079, 20079], + "copied_bytes": [743384, 743384], + "promoted_objects": [3390, 3390], + "promoted_bytes": [122120, 122120], + "freed_bytes": [26862320, 26862320] + } + }, + "06_string_retention": { + "stdout": "probe:06_string_retention\nchecksum:1112804\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1072, 1072, 1072], + "heap_total_bytes": [22020096, 22020096, 22020096], + "minor_cycles": [4, 4], + "step_cycles": [4, 4], + "copied_objects": [298, 298], + "copied_bytes": [72288, 72288], + "promoted_objects": [14, 14], + "promoted_bytes": [1072, 1072], + "freed_bytes": [68136816, 68136816] + } + }, + "07_array_grow_evacuate": { + "stdout": "probe:07_array_grow_evacuate\nchecksum:21891160\nsurvivors:94\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [1072456, 1072456, 1072456], + "heap_total_bytes": [25165824, 25165824, 25165824], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [4991, 4991], + "copied_bytes": [2404720, 2404720], + "promoted_objects": [4769, 4769], + "promoted_bytes": [623120, 623120], + "freed_bytes": [83788528, 83788528] + } + }, + "08_map_set_sidetables": { + "stdout": "probe:08_map_set_sidetables\nchecksum:1109446656\nfinal_sizes:0,0\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [688, 688, 688], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [6, 6], + "step_cycles": [6, 6], + "copied_objects": [2489, 2489], + "copied_bytes": [99656, 99656], + "promoted_objects": [14, 14], + "promoted_bytes": [592, 592], + "freed_bytes": [56598904, 56598904] + } + }, + "09_try_catch_roots": { + "stdout": "probe:09_try_catch_roots\nchecksum:-1465100498\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [247392, 247392, 247392], + "heap_total_bytes": [14680064, 14680064, 14680064], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4962, 4962], + "copied_bytes": [230352, 230352], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [10255312, 10255312] + } + }, + "10_store_receiver_across_alloc": { + "stdout": "probe:10_store_receiver_across_alloc\nchecksum:-1837890048\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [244648, 244648, 244648], + "heap_total_bytes": [16777216, 16777216, 16777216], + "minor_cycles": [9, 9], + "step_cycles": [9, 9], + "copied_objects": [18660, 18660], + "copied_bytes": [825520, 825520], + "promoted_objects": [4722, 4722], + "promoted_bytes": [228440, 228440], + "freed_bytes": [88858544, 88858544] + } + }, + "11_collect_at_depth": { + "stdout": "probe:11_collect_at_depth\nchecksum:-1569838524\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [247712, 247712, 247712], + "heap_total_bytes": [12582912, 12582912, 12582912], + "minor_cycles": [1, 1], + "step_cycles": [1, 1], + "copied_objects": [4975, 4975], + "copied_bytes": [230872, 230872], + "promoted_objects": [0, 0], + "promoted_bytes": [0, 0], + "freed_bytes": [9206240, 9206240] + } + }, + "12_large_live_set": { + "stdout": "probe:12_large_live_set\nchecksum:1678627152\nwalked:700000\nsum:186514128\nkept:10938\nkeptSum:-466842304\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": {}, + "samples": { + "heap_used_bytes": [569448, 569448, 569448], + "heap_total_bytes": [72351744, 72351744, 72351744], + "minor_cycles": [5, 5], + "step_cycles": [5, 5], + "copied_objects": [58175, 58175], + "copied_bytes": [2327008, 2327008], + "promoted_objects": [530030, 530030], + "promoted_bytes": [21201208, 21201208], + "freed_bytes": [63732784, 63732784] + } + }, + "13_large_eden_survivors": { + "stdout": "probe:13_large_eden_survivors\nchecksum:1846988256\nhits:3000000\ntagChars:1722368\npeerIds:-131072\nnotes:11719\nnoteSum:22499072\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "64" + }, + "samples": { + "heap_used_bytes": [227048, 227048, 227048], + "heap_total_bytes": [61865984, 61865984, 61865984], + "minor_cycles": [3, 3], + "step_cycles": [3, 3], + "copied_objects": [377569, 377569], + "copied_bytes": [21429712, 21429712], + "promoted_objects": [386271, 386271], + "promoted_bytes": [21777792, 21777792], + "freed_bytes": [108243696, 108243696] + } + }, + "14_grow_then_churn": { + "stdout": "probe:14_grow_then_churn\ncacheSum:2050177040\nchurnSum:815300000\nliveSum:2008997952\n", + "correctness": { + "status": "pass", + "oracle_version": "v26.5.1", + "reason": "" + }, + "run_env": { + "PERRY_GC_SCAVENGE_NURSERY_MB": "1", + "PERRY_GC_MAJOR_PACING_FLOOR_MB": "1" + }, + "samples": { + "heap_used_bytes": [430584, 430584, 430584], + "heap_total_bytes": [23068672, 23068672, 23068672], + "minor_cycles": [22, 22], + "step_cycles": [23, 23], + "copied_objects": [330, 330], + "copied_bytes": [1277072, 1277072], + "promoted_objects": [406707, 406707], + "promoted_bytes": [147967960, 147967960], + "freed_bytes": [119606864, 119606864] + } + } + } + }, + "finding": "All deterministic medians unchanged; do not attribute any accepted cell to #9514." + }, + "capacity_variance": { + "generated_at": "2026-09-06T04:11:20+00:00", + "probe": "13_large_eden_survivors", + "probe_sha256": "07f64b9d0c299fc5d7b0ca1dc625e11d5b101ff04fa904bf981edaaae9150676", + "binary_sha256": "6b2bbe0bc23f9e3d1f88b8c32e822880569c0c81c550807e360ddbedf6af75c8", + "runs": [ + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163495936, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163229696, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163131392, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163254272, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 63963136, + "rss_bytes": 161820672, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163164160, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163336192, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163438592, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163450880, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163344384, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163360768, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163606528, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163405824, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163381248, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163532800, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163078144, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 63963136, + "rss_bytes": 161935360, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 63963136, + "rss_bytes": 161914880, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163110912, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163225600, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + }, + { + "heap_used_bytes": 227048, + "heap_total_bytes": 61865984, + "rss_bytes": 163471360, + "minor_cycles": 3, + "step_cycles": 3, + "copied_objects": 377569, + "copied_bytes": 21429712, + "promoted_objects": 386271, + "promoted_bytes": 21777792, + "freed_bytes": 108243696 + } + ], + "distributions": { + "heap_used_bytes": { + "samples": [227048, 227048, 227048, 227048, 227048, 227048, 227048, 227048, 227048, 227048, 227048, 227048, 227048, 227048, 227048, 227048, 227048, 227048, 227048, 227048, 227048], + "sample_count": 21, + "median": 227048, + "min": 227048, + "max": 227048, + "stdev": 0, + "spread": 0, + "spread_pct": 0 + }, + "heap_total_bytes": { + "samples": [61865984, 61865984, 61865984, 61865984, 63963136, 61865984, 61865984, 61865984, 61865984, 61865984, 61865984, 61865984, 61865984, 61865984, 61865984, 61865984, 63963136, 63963136, 61865984, 61865984, 61865984], + "sample_count": 21, + "median": 61865984, + "min": 61865984, + "max": 63963136, + "stdev": 733850.330437, + "spread": 2097152, + "spread_pct": 3.389831 + }, + "rss_bytes": { + "samples": [163495936, 163229696, 163131392, 163254272, 161820672, 163164160, 163336192, 163438592, 163450880, 163344384, 163360768, 163606528, 163405824, 163381248, 163532800, 163078144, 161935360, 161914880, 163110912, 163225600, 163471360], + "sample_count": 21, + "median": 163336192, + "min": 161820672, + "max": 163606528, + "stdev": 524430.638193, + "spread": 1785856, + "spread_pct": 1.093362 + }, + "minor_cycles": { + "samples": [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + "sample_count": 21, + "median": 3, + "min": 3, + "max": 3, + "stdev": 0, + "spread": 0, + "spread_pct": 0 + }, + "step_cycles": { + "samples": [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + "sample_count": 21, + "median": 3, + "min": 3, + "max": 3, + "stdev": 0, + "spread": 0, + "spread_pct": 0 + }, + "copied_objects": { + "samples": [377569, 377569, 377569, 377569, 377569, 377569, 377569, 377569, 377569, 377569, 377569, 377569, 377569, 377569, 377569, 377569, 377569, 377569, 377569, 377569, 377569], + "sample_count": 21, + "median": 377569, + "min": 377569, + "max": 377569, + "stdev": 0, + "spread": 0, + "spread_pct": 0 + }, + "copied_bytes": { + "samples": [21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712, 21429712], + "sample_count": 21, + "median": 21429712, + "min": 21429712, + "max": 21429712, + "stdev": 0, + "spread": 0, + "spread_pct": 0 + }, + "promoted_objects": { + "samples": [386271, 386271, 386271, 386271, 386271, 386271, 386271, 386271, 386271, 386271, 386271, 386271, 386271, 386271, 386271, 386271, 386271, 386271, 386271, 386271, 386271], + "sample_count": 21, + "median": 386271, + "min": 386271, + "max": 386271, + "stdev": 0, + "spread": 0, + "spread_pct": 0 + }, + "promoted_bytes": { + "samples": [21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792, 21777792], + "sample_count": 21, + "median": 21777792, + "min": 21777792, + "max": 21777792, + "stdev": 0, + "spread": 0, + "spread_pct": 0 + }, + "freed_bytes": { + "samples": [108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696, 108243696], + "sample_count": 21, + "median": 108243696, + "min": 108243696, + "max": 108243696, + "stdev": 0, + "spread": 0, + "spread_pct": 0 + } + }, + "stdout": "probe:13_large_eden_survivors\nchecksum:1846988256\nhits:3000000\ntagChars:1722368\npeerIds:-131072\nnotes:11719\nnoteSum:22499072\n", + "correctness": "21/21 equal pinned Node oracle output", + "env": { + "PERRY_GC_DIAG": "1", + "PERRY_GC_SCAVENGE_NURSERY_MB": "64" + } + }, + "accepted_cells": [ + { + "probe": "01_nursery_churn", + "metric": "copied_objects", + "previous_median": 6354.0, + "accepted_median": 4977.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "01_nursery_churn", + "metric": "copied_bytes", + "previous_median": 430008.0, + "accepted_median": 234536.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "01_nursery_churn", + "metric": "freed_bytes", + "previous_median": 13201144.0, + "accepted_median": 11299648.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "02_survivor_promotion", + "metric": "copied_bytes", + "previous_median": 1780080.0, + "accepted_median": 1450992.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140" + ] + }, + { + "probe": "03_cross_gen_writes", + "metric": "freed_bytes", + "previous_median": 11304368.0, + "accepted_median": 9240008.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140" + ] + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "copied_objects", + "previous_median": 450.0, + "accepted_median": 3622.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b" + ] + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "copied_bytes", + "previous_median": 25832.0, + "accepted_median": 149400.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b" + ] + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "promoted_objects", + "previous_median": 10.0, + "accepted_median": 682.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b" + ] + }, + { + "probe": "04_dead_after_deep_stack", + "metric": "freed_bytes", + "previous_median": 83880432.0, + "accepted_median": 73346416.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b" + ] + }, + { + "probe": "05_closure_capture", + "metric": "copied_objects", + "previous_median": 5272.0, + "accepted_median": 20079.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "05_closure_capture", + "metric": "copied_bytes", + "previous_median": 233384.0, + "accepted_median": 743384.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "05_closure_capture", + "metric": "promoted_objects", + "previous_median": 0.0, + "accepted_median": 3390.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "05_closure_capture", + "metric": "promoted_bytes", + "previous_median": 0.0, + "accepted_median": 122120.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "05_closure_capture", + "metric": "freed_bytes", + "previous_median": 32422176.0, + "accepted_median": 26862320.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "06_string_retention", + "metric": "copied_objects", + "previous_median": 11138.0, + "accepted_median": 298.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "06_string_retention", + "metric": "copied_bytes", + "previous_median": 682240.0, + "accepted_median": 72288.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "06_string_retention", + "metric": "promoted_objects", + "previous_median": 4960.0, + "accepted_median": 14.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "06_string_retention", + "metric": "promoted_bytes", + "previous_median": 248512.0, + "accepted_median": 1072.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "06_string_retention", + "metric": "freed_bytes", + "previous_median": 75244808.0, + "accepted_median": 68136816.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a" + ] + }, + { + "probe": "07_array_grow_evacuate", + "metric": "heap_used_bytes", + "previous_median": 984840.0, + "accepted_median": 1072456.0, + "causes": [ + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "5196d830fd0689937db94598d94662d531d4ee92" + ] + }, + { + "probe": "07_array_grow_evacuate", + "metric": "heap_total_bytes", + "previous_median": 23068672.0, + "accepted_median": 25165824.0, + "causes": [ + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "5196d830fd0689937db94598d94662d531d4ee92" + ] + }, + { + "probe": "07_array_grow_evacuate", + "metric": "copied_objects", + "previous_median": 6387.0, + "accepted_median": 4991.0, + "causes": [ + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "5196d830fd0689937db94598d94662d531d4ee92" + ] + }, + { + "probe": "08_map_set_sidetables", + "metric": "copied_objects", + "previous_median": 3017.0, + "accepted_median": 2489.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140" + ] + }, + { + "probe": "09_try_catch_roots", + "metric": "copied_objects", + "previous_median": 0.0, + "accepted_median": 4962.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "d923b8dcf08f2afed71777c33423362411039cf0", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "09_try_catch_roots", + "metric": "copied_bytes", + "previous_median": 0.0, + "accepted_median": 230352.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "d923b8dcf08f2afed71777c33423362411039cf0", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "09_try_catch_roots", + "metric": "promoted_objects", + "previous_median": 6240.0, + "accepted_median": 0.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "d923b8dcf08f2afed71777c33423362411039cf0", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "09_try_catch_roots", + "metric": "promoted_bytes", + "previous_median": 421800.0, + "accepted_median": 0.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "d923b8dcf08f2afed71777c33423362411039cf0", + "aa8d2ade03970ac7eb2065f3eb0e18bb4d2df1c7" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "heap_total_bytes", + "previous_median": 12582912.0, + "accepted_median": 16777216.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "minor_cycles", + "previous_median": 1.0, + "accepted_median": 9.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "step_cycles", + "previous_median": 1.0, + "accepted_median": 9.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "copied_objects", + "previous_median": 8160.0, + "accepted_median": 18660.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "copied_bytes", + "previous_median": 506200.0, + "accepted_median": 825520.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "promoted_objects", + "previous_median": 0.0, + "accepted_median": 4722.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "promoted_bytes", + "previous_median": 0.0, + "accepted_median": 228440.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "10_store_receiver_across_alloc", + "metric": "freed_bytes", + "previous_median": 8930928.0, + "accepted_median": 88858544.0, + "causes": [ + "22c07ac5b44c5ff6a988257067110c7451da0223" + ] + }, + { + "probe": "11_collect_at_depth", + "metric": "copied_objects", + "previous_median": 6245.0, + "accepted_median": 4975.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "d923b8dcf08f2afed71777c33423362411039cf0" + ] + }, + { + "probe": "11_collect_at_depth", + "metric": "copied_bytes", + "previous_median": 422744.0, + "accepted_median": 230872.0, + "causes": [ + "b3e88f1fb3378fe30b065ed328b73ea0527ab18a", + "fc8c9e9a15902f889df2b1aeb280ca6223f6948a", + "d923b8dcf08f2afed71777c33423362411039cf0" + ] + }, + { + "probe": "12_large_live_set", + "metric": "copied_bytes", + "previous_median": 2851312.0, + "accepted_median": 2327008.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140" + ] + }, + { + "probe": "12_large_live_set", + "metric": "promoted_bytes", + "previous_median": 25395472.0, + "accepted_median": 21201208.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140" + ] + }, + { + "probe": "12_large_live_set", + "metric": "freed_bytes", + "previous_median": 76315456.0, + "accepted_median": 63732784.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140" + ] + }, + { + "probe": "13_large_eden_survivors", + "metric": "copied_objects", + "previous_median": 0.0, + "accepted_median": 377569.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "e18b24c420c7c2f389607537618699ffc6aa9d1d", + "d923b8dcf08f2afed71777c33423362411039cf0" + ] + }, + { + "probe": "13_large_eden_survivors", + "metric": "copied_bytes", + "previous_median": 0.0, + "accepted_median": 21429712.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "e18b24c420c7c2f389607537618699ffc6aa9d1d", + "d923b8dcf08f2afed71777c33423362411039cf0" + ] + }, + { + "probe": "13_large_eden_survivors", + "metric": "promoted_objects", + "previous_median": 541614.0, + "accepted_median": 386271.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "e18b24c420c7c2f389607537618699ffc6aa9d1d", + "d923b8dcf08f2afed71777c33423362411039cf0" + ] + }, + { + "probe": "13_large_eden_survivors", + "metric": "promoted_bytes", + "previous_median": 30316904.0, + "accepted_median": 21777792.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "e18b24c420c7c2f389607537618699ffc6aa9d1d", + "d923b8dcf08f2afed71777c33423362411039cf0" + ] + }, + { + "probe": "13_large_eden_survivors", + "metric": "freed_bytes", + "previous_median": 97086288.0, + "accepted_median": 108243696.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "e18b24c420c7c2f389607537618699ffc6aa9d1d", + "d923b8dcf08f2afed71777c33423362411039cf0" + ] + }, + { + "probe": "14_grow_then_churn", + "metric": "copied_objects", + "previous_median": 307.0, + "accepted_median": 330.0, + "causes": [ + "e2e6f4a1cfded098ca05afe7e1b4e5ce783a5140", + "06e1ab349c8c6e40587bb3ce2ee9f1c58b20522b", + "e18b24c420c7c2f389607537618699ffc6aa9d1d" + ] + } + ] +} diff --git a/benchmarks/gc_ratchet/evidence/9829-recovery.md b/benchmarks/gc_ratchet/evidence/9829-recovery.md new file mode 100644 index 0000000000..2cae958f01 --- /dev/null +++ b/benchmarks/gc_ratchet/evidence/9829-recovery.md @@ -0,0 +1,176 @@ +# GC ratchet recovery: issue #9829 + +The shared-CI gate is restored by repairing probe 10, accepting 46 specific +deterministic counter deltas, and excluding one demonstrated noisy capacity cell. +Every other distribution and every tolerance band is retained. The original +macOS RSS/timing pin is preserved; the resized workload needs a separate timing +capture on its original quiet host before using the pinned_host profile. + +This is the internal Perry-vs-Perry GC artifact. The public Node/Bun performance +baseline and package versions are unchanged. + +## Provenance and acceptance boundary + +The pre-fix subject is main d36a1af0c205ebdc7cf7f75b351ea34b5bc0fc0b and +[scheduled run 33989581881](https://github.com/PerryTS/perry/actions/runs/33989581881). +A fresh Linux x86-64 compiler and both static archive wrappers reproduce all +126 deterministic **medians** from macOS CI, including all 43 failing rows. +125 complete distributions match; CI's large-Eden capacity has one +2 MiB sample. +The same variation appears locally, so it was investigated rather than discarded. + +The gate failed unwatched from 2026-08-18 to 2026-09-06. Its failing cell count +moved 43 -> 48 -> 43, with intervening harness failures. This pin is one sample +of a moving set, not accumulated independent regressions. + +[9829-recovery.json](9829-recovery.json) contains original deterministic samples, +Node correctness results, source and binary hashes, eight historical builds +(four adjacent-commit A/Bs), the protected-probe sabotage control, the retention +classification, and 21 executions of the capacity probe. All builds use the same +package set: perry, perry-runtime-static, perry-stdlib-static, in release with +codegen-units=16 and LTO disabled. Measurement pins the runtime archive directory +and disables automatic workload-specific archive rebuilding. + +The receipt distinguishes **measured** changes from **bracketed** windows. +The latter are the accepted window-level explanations from the +[issue's completed classification](https://github.com/PerryTS/perry/issues/9829#issuecomment-5555805084), +not claims that an individual candidate was bisected. Repeating that distinction +in the machine-readable causes prevents a future reader treating every candidate +as established fact. + +## What the measurements establish + +- **#8313, smaller objects:** the first 19 crossings were already bisected + [byte-exact in the issue](https://github.com/PerryTS/perry/issues/9829#issuecomment-5555657705). + Smaller allocations lower byte traffic and change which cohort reaches a minor. + No post-release heap_used increase on probe 12 is accepted here: it returned + inside its original band. +- **#8657, array growth generation:** our c2da03439 -> 06e1ab349 build pair + reproduces the large 04/05 copy/promotion jumps. Probe 04 reaches exactly today's + 149400 copied bytes and 682 promoted objects. The safety fix keeps old array + growth targets out of the nursery and avoids collection while growing a young + array. Probe 07 capacity rises by 2 MiB and probe 14 copied objects move 340 -> 330. +- **#8806, transition cache:** our 066f9d595 -> b3e88f1fb pair reproduces all four + August 26 crossing values exactly: 01 copied=6684; 06 copied=11714; + 09 promoted=6560; 11 copied=6562. +- **#8900, weak targets:** our d02892491 -> e18b24c42 pair confirms part of the + later drift: probe 10 copied objects fall 8487 -> 7457 and copied bytes fall + 529144 -> 327328. The transition target becomes a rewrite-only edge, with dead + cache entries pruned. It does **not** explain every cell in that window. +- **#9373, short-concat memo:** our 3fc54c441 -> d923b8dcf pair moves probe 13 + from no copies to 356717 copies / 20594360 copied bytes, with promotions falling + 531314 -> 365297. Reused row tags change nursery occupancy and collection + cohorts under the existing policy. The earlier #9359 candidate was incorrect: + it adds checks behind PERRY_GC_VERIFY_MARK. Later values have further offsets. +- **#9514 control:** compiling current main with PERRY_CONCAT_SITE_CACHE=0 changes + no deterministic median in these probes. It is not used as an explanation. +- **Retention classification:** current main with the repaired probe has zero + conservative-stack excess on all 14 probes. Probe 07's accepted live-byte tail + is real (+87616 bytes), classified as the allocation/packing tradeoff in the + September 4 window; it is not described as a memory improvement or an exact + single-commit attribution. Its live bytes remain gated after this acceptance. + +## Probe 10 must collect + +The included #9833 repair raises the workload from 200000 to 2400000 iterations. +Seven-repeat measurements give nine minors, 18660 copied objects, 4722 promoted +objects, and 88858544 freed bytes. Live bytes fall 464072 -> 244648, already +inside the original 220384 + 65536 band, so that cell is **not** re-pinned. + +The fixed probe passes with PERRY_GC_PROTECT_FROMSPACE=1 and depth=64. +Removing only its allocating RHS yields zero minor cycles, copies, promotions, +and freed bytes. This independently demonstrates the allocating store window; +stdout alone would not establish evacuation coverage. + +## Probe 13 capacity is not deterministic + +One unchanged binary, 21 runs under its declared 64 MiB nursery: + +| metric | observed | +|---|---:| +| heap_total_bytes | 61865984 (18 runs), 63963136 (3 runs) | +| heap_used_bytes | 227048 (21/21) | +| minor_cycles / step_cycles | 3 / 3 (21/21) | +| copied_objects / copied_bytes | 377569 / 21429712 (21/21) | +| promoted_objects / promoted_bytes | 386271 / 21777792 (21/21) | +| freed_bytes | 108243696 (21/21) | +| Node-equivalent stdout | 21/21 | + +arena/stats.rs defines heapTotal as reserved block capacity and heapUsed as the +live-object census. Capacity changes with identical live bytes and collector +work in the same binary; the exact allocation/block-order source of the spare +2 MiB is not isolated. CI independently exhibits the same two capacities. +Choosing seven runs without the outlier would conceal the premise failure. +Only this probe's heap_total_bytes becomes informational through the existing +evidence-validated probe_overrides mechanism. It is still measured and printed; +its live bytes, correctness, collection liveness, and all GC counters remain +gated. No band is widened and the old capacity distribution is preserved. + +## Accepted cells + +Only the cells below change. A row containing a bracketed cause retains that +evidence grade even when another part of its history was measured exactly. +Cause descriptions and full commit hashes are in accepted_deterministic_deltas; +that receipt has no role in granting tolerance allowances. + +| Probe | Metric | Previous | Accepted | Evidence / PRs | +|---|---|---:|---:|---| +| 01_nursery_churn | copied_objects | 6,354 | 4,977 | bracketed + measured: #8313, #8806, #8887 | +| 01_nursery_churn | copied_bytes | 430,008 | 234,536 | bracketed + measured: #8313, #8806, #8887 | +| 01_nursery_churn | freed_bytes | 13,201,144 | 11,299,648 | bracketed + measured: #8313, #8806, #8887 | +| 02_survivor_promotion | copied_bytes | 1,780,080 | 1,450,992 | measured: #8313 | +| 03_cross_gen_writes | freed_bytes | 11,304,368 | 9,240,008 | measured: #8313 | +| 04_dead_after_deep_stack | copied_objects | 450 | 3,622 | measured: #8313, #8657 | +| 04_dead_after_deep_stack | copied_bytes | 25,832 | 149,400 | measured: #8313, #8657 | +| 04_dead_after_deep_stack | promoted_objects | 10 | 682 | measured: #8313, #8657 | +| 04_dead_after_deep_stack | freed_bytes | 83,880,432 | 73,346,416 | measured: #8313, #8657 | +| 05_closure_capture | copied_objects | 5,272 | 20,079 | bracketed + measured: #8313, #8657, #9628 | +| 05_closure_capture | copied_bytes | 233,384 | 743,384 | bracketed + measured: #8313, #8657, #9628 | +| 05_closure_capture | promoted_objects | 0 | 3,390 | bracketed + measured: #8313, #8657, #9628 | +| 05_closure_capture | promoted_bytes | 0 | 122,120 | bracketed + measured: #8313, #8657, #9628 | +| 05_closure_capture | freed_bytes | 32,422,176 | 26,862,320 | bracketed + measured: #8313, #8657, #9628 | +| 06_string_retention | copied_objects | 11,138 | 298 | bracketed + measured: #8806, #8887 | +| 06_string_retention | copied_bytes | 682,240 | 72,288 | bracketed + measured: #8806, #8887 | +| 06_string_retention | promoted_objects | 4,960 | 14 | bracketed + measured: #8806, #8887 | +| 06_string_retention | promoted_bytes | 248,512 | 1,072 | bracketed + measured: #8806, #8887 | +| 06_string_retention | freed_bytes | 75,244,808 | 68,136,816 | bracketed + measured: #8806, #8887 | +| 07_array_grow_evacuate | heap_used_bytes | 984,840 | 1,072,456 | bracketed + measured: #8657, #8887, #9706 | +| 07_array_grow_evacuate | heap_total_bytes | 23,068,672 | 25,165,824 | bracketed + measured: #8657, #8887, #9706 | +| 07_array_grow_evacuate | copied_objects | 6,387 | 4,991 | bracketed + measured: #8657, #8887, #9706 | +| 08_map_set_sidetables | copied_objects | 3,017 | 2,489 | measured: #8313 | +| 09_try_catch_roots | copied_objects | 0 | 4,962 | bracketed + measured: #8806, #8887, #9373, #9628 | +| 09_try_catch_roots | copied_bytes | 0 | 230,352 | bracketed + measured: #8806, #8887, #9373, #9628 | +| 09_try_catch_roots | promoted_objects | 6,240 | 0 | bracketed + measured: #8806, #8887, #9373, #9628 | +| 09_try_catch_roots | promoted_bytes | 421,800 | 0 | bracketed + measured: #8806, #8887, #9373, #9628 | +| 10_store_receiver_across_alloc | heap_total_bytes | 12,582,912 | 16,777,216 | measured: #9833 | +| 10_store_receiver_across_alloc | minor_cycles | 1 | 9 | measured: #9833 | +| 10_store_receiver_across_alloc | step_cycles | 1 | 9 | measured: #9833 | +| 10_store_receiver_across_alloc | copied_objects | 8,160 | 18,660 | measured: #9833 | +| 10_store_receiver_across_alloc | copied_bytes | 506,200 | 825,520 | measured: #9833 | +| 10_store_receiver_across_alloc | promoted_objects | 0 | 4,722 | measured: #9833 | +| 10_store_receiver_across_alloc | promoted_bytes | 0 | 228,440 | measured: #9833 | +| 10_store_receiver_across_alloc | freed_bytes | 8,930,928 | 88,858,544 | measured: #9833 | +| 11_collect_at_depth | copied_objects | 6,245 | 4,975 | bracketed + measured: #8806, #8887, #9373 | +| 11_collect_at_depth | copied_bytes | 422,744 | 230,872 | bracketed + measured: #8806, #8887, #9373 | +| 12_large_live_set | copied_bytes | 2,851,312 | 2,327,008 | measured: #8313 | +| 12_large_live_set | promoted_bytes | 25,395,472 | 21,201,208 | measured: #8313 | +| 12_large_live_set | freed_bytes | 76,315,456 | 63,732,784 | measured: #8313 | +| 13_large_eden_survivors | copied_objects | 0 | 377,569 | measured: #8313, #8900, #9373 | +| 13_large_eden_survivors | copied_bytes | 0 | 21,429,712 | measured: #8313, #8900, #9373 | +| 13_large_eden_survivors | promoted_objects | 541,614 | 386,271 | measured: #8313, #8900, #9373 | +| 13_large_eden_survivors | promoted_bytes | 30,316,904 | 21,777,792 | measured: #8313, #8900, #9373 | +| 13_large_eden_survivors | freed_bytes | 97,086,288 | 108,243,696 | measured: #8313, #8900, #9373 | +| 14_grow_then_churn | copied_objects | 307 | 330 | measured: #8313, #8657, #8900 | + +## Reporting and harness integrity + +distribution() now rounds samples before deriving summaries, so saving and +reloading timing data cannot manufacture a corrupt-artifact error (#9834). +The regression cases fail before the fix, including a median-rounding case. + +Scheduled and main-branch dispatch runs now maintain one open GC ratchet issue, +with the run/SHA, last green SHA, failed steps, regression rows, and added/cleared +cell names. A later successful main run closes it. Missing measurement logs +still produce an incident with the failing job link. PRs, tags, cancelled runs, +and dispatches on other branches cannot write issues. Reporting uses a separate, +serialized job with its own issues:write token; it does not change the gate's +exit status. Extending this to other workflows remains the separate #9830 scope. diff --git a/benchmarks/gc_ratchet/tolerances.json b/benchmarks/gc_ratchet/tolerances.json index 4122e3f927..ab2d5b7fdc 100644 --- a/benchmarks/gc_ratchet/tolerances.json +++ b/benchmarks/gc_ratchet/tolerances.json @@ -45,9 +45,11 @@ "2026-08-08 re-pin every probe's conservative reading equals its precise one", "(gc_ratchet.py classify, excess 0.00% on all twelve), so heap_used_bytes now", "means what it says. `classify` is still the first step on a breach -- it is", - "now also the check that the term has not come back." + "now also the check that the term has not come back.", + "#9829 excludes only 13_large_eden_survivors.heap_total_bytes after 21 executions", + "of one binary varied by 2 MiB of reserved capacity with identical live bytes", + "and all seven GC counters. This is not a wider band or a retention exemption." ], - "shared_ci": { "heap_used_bytes": { "pct": 2.0, @@ -134,7 +136,6 @@ "rationale": "EXCLUDED FROM THE SHARED-CI GATE. Wall time on a shared runner is dominated by neighbour noise; no band both survives that and catches a real GC slowdown. Rather than widen it until it cannot fire, it is marked non-gating and reported. The GC-work dimension is covered on this runner by the deterministic counters (minor_cycles, copied_objects, freed_bytes), which measure how much work the collector did without measuring how fast the machine was." } }, - "pinned_host": { "heap_used_bytes": { "pct": 2.0, @@ -221,7 +222,6 @@ "rationale": "GATED HERE ONLY. Worst cross-session spread of medians-of-7 was 0.751% on an idle box (load 1.7-2.0); worst raw within-session spread was 5.3%, which the median-of-7 damps out. 10% is ~13x the cross-session figure and ~2x the worst raw spread, so it will not fire on scheduler jitter but will catch the tens-of-percent slowdown a whole-stack conservative scan would introduce. The 15 ms floor covers the fastest probe (126 ms)." } }, - "probe_overrides": { "07_array_grow_evacuate": { "copied_bytes": { @@ -244,6 +244,18 @@ "issue": "https://github.com/PerryTS/perry/issues/9790" } } + }, + "13_large_eden_survivors": { + "heap_total_bytes": { + "gating": false, + "rationale": "NOT GATED ON THIS PROBE (#9829). Total reserved arena capacity varies by 2 MiB across executions of one unchanged binary: 18/21 runs reserve 61865984 bytes and 3/21 reserve 63963136. All 21 report the same 227048 live bytes and identical seven GC counters (including three minors and 377569 copied objects), with Node-equivalent output. js_arena_stats sums reserved block sizes for heapTotal, separately from its exact live-object census for heapUsed. This is spare-capacity/placement variation, not changing live retention or GC work; the precise allocating block/order is not isolated. CI run 33989581881 independently contains the same two capacities. Keep the cell visible, while heapUsed, liveness, copying/promotion/reclamation, and all other probes remain gated. Raw 21-run evidence and binary/source hashes: evidence/9829-recovery.json.", + "evidence": { + "observed_runs": 21, + "observed_spread": 2097152, + "measured_on": "2026-09-06, linux-x86_64, clean main d36a1af0c compiler and matching runtime/stdlib archives. One unchanged probe binary, 21 traced executions with PERRY_GC_SCAVENGE_NURSERY_MB=64. See evidence/9829-recovery.json capacity_variance.", + "issue": "https://github.com/PerryTS/perry/issues/9829" + } + } } } } diff --git a/changelog.d/9829-gc-ratchet-recovery.md b/changelog.d/9829-gc-ratchet-recovery.md new file mode 100644 index 0000000000..58f6f3443c --- /dev/null +++ b/changelog.d/9829-gc-ratchet-recovery.md @@ -0,0 +1 @@ +Restore the shared-CI GC ratchet after the unwatched main drift: repair the inert receiver-store probe, fix summary rounding, and accept 46 counter changes with measured or explicitly bracketed per-cell provenance. Record a 21-run exclusion for the large-Eden reserved-capacity cell, keeping live bytes and GC work gated. Scheduled/main-dispatch failures now maintain one incident with the last green SHA and changing regression rows; recovery closes it. Package versions and the public performance baseline are unchanged. See benchmarks/gc_ratchet/evidence/9829-recovery.md for measurements and validation. diff --git a/tests/test_gc_ratchet.py b/tests/test_gc_ratchet.py index db778a95c6..e0ccdf7267 100644 --- a/tests/test_gc_ratchet.py +++ b/tests/test_gc_ratchet.py @@ -685,11 +685,9 @@ def test_a_full_re_pin_without_a_receipt_is_valid(self): so pin the permission as a test rather than a comment. """ artifact = json.loads(DEFAULT_ARTIFACT.read_text(encoding="utf-8")) - self.assertNotIn( - "accepted_deterministic_deltas", - artifact, - "the pinned baseline is a full re-pin; update this test if that changes", - ) + # Exercise the full-pin contract independently of whether today's + # checked-in artifact is full or selective (#9829). + artifact.pop("accepted_deterministic_deltas", None) validate_artifact(artifact) def test_a_receipt_on_the_pin_must_name_real_cells_and_real_causes(self): @@ -852,6 +850,48 @@ class ProbeOverrideTests(unittest.TestCase): about the ways an exclusion is refused. """ + def test_large_eden_capacity_exclusion_has_live_same_binary_evidence(self): + evidence_path = REPO_ROOT / "benchmarks/gc_ratchet/evidence/9829-recovery.json" + evidence = json.loads(evidence_path.read_text(encoding="utf-8"))["capacity_variance"] + runs = evidence["runs"] + self.assertGreaterEqual(len(runs), MIN_EXCLUSION_RUNS) + self.assertRegex(evidence["binary_sha256"], r"^[0-9a-f]{64}$") + self.assertGreater(len({run["heap_total_bytes"] for run in runs}), 1) + for metric in ("heap_used_bytes",) + GC_METRICS: + self.assertEqual(len({run[metric] for run in runs}), 1, metric) + self.assertGreater(runs[0]["minor_cycles"], 0) + self.assertGreater(runs[0]["copied_objects"], 0) + override = _shipped_tolerances()["probe_overrides"]["13_large_eden_survivors"] + self.assertEqual(set(override), {"heap_total_bytes"}) + capacity = [run["heap_total_bytes"] for run in runs] + self.assertEqual(override["heap_total_bytes"]["evidence"]["observed_runs"], len(runs)) + self.assertEqual( + override["heap_total_bytes"]["evidence"]["observed_spread"], + max(capacity) - min(capacity), + ) + + def test_large_eden_capacity_exclusion_preserves_live_byte_and_gc_gates(self): + baseline = json.loads(DEFAULT_ARTIFACT.read_text(encoding="utf-8")) + current = _measurement(copy.deepcopy(baseline["probes"])) + probe = "13_large_eden_survivors" + value = current["probes"][probe]["metrics"]["heap_total_bytes"]["median"] + current["probes"][probe]["metrics"]["heap_total_bytes"] = distribution([value * 2] * 7) + rows, failures = evaluate(baseline, current, profile="shared_ci") + self.assertEqual(_hard(failures), []) + capacity = next(r for r in rows if r.probe == probe and r.metric == "heap_total_bytes") + self.assertEqual(capacity.status, "drift (informational)") + for metric in ("heap_used_bytes", "copied_objects"): + with self.subTest(metric=metric): + changed = copy.deepcopy(current) + value = changed["probes"][probe]["metrics"][metric]["median"] + changed["probes"][probe]["metrics"][metric] = distribution([value + 1000000] * 7) + rows, failures = evaluate(baseline, changed, profile="shared_ci") + self.assertTrue(_hard(failures)) + self.assertTrue(any( + r.probe == probe and r.metric == metric and r.status == "REGRESSION" + for r in rows + )) + def test_an_overridden_cell_cannot_fail_the_job(self): baseline = _baseline(_pair(), _with_override()) current = _measurement(_pair({"heap_used_bytes": 9_000_000.0})) From 075a51caabd229b5019b98b3fd9db0948f84d69c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 6 Sep 2026 04:24:55 +0000 Subject: [PATCH 10/11] docs: key GC ratchet recovery changelog to PR 9837 --- .../{9829-gc-ratchet-recovery.md => 9837-gc-ratchet-recovery.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog.d/{9829-gc-ratchet-recovery.md => 9837-gc-ratchet-recovery.md} (100%) diff --git a/changelog.d/9829-gc-ratchet-recovery.md b/changelog.d/9837-gc-ratchet-recovery.md similarity index 100% rename from changelog.d/9829-gc-ratchet-recovery.md rename to changelog.d/9837-gc-ratchet-recovery.md From 12f4e27948883392911632e5968154728c2b048d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 6 Sep 2026 11:15:14 +0200 Subject: [PATCH 11/11] style: cargo fmt --- crates/perry-runtime/src/arena/mod.rs | 4 ++-- crates/perry-runtime/src/gc/copying.rs | 3 +-- crates/perry-runtime/src/gc/policy.rs | 20 ++++++++++++++++---- crates/perry-runtime/src/gc/tests/mod.rs | 4 ++-- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/crates/perry-runtime/src/arena/mod.rs b/crates/perry-runtime/src/arena/mod.rs index fbc359df41..12f68b2271 100644 --- a/crates/perry-runtime/src/arena/mod.rs +++ b/crates/perry-runtime/src/arena/mod.rs @@ -34,6 +34,8 @@ mod tests_promoted_runs; pub(crate) use allocators::{ inactive_survivor_index, with_survivor_arena, with_survivor_arena_mut, }; +#[cfg(test)] +pub(crate) use block::old_gen_in_use_bytes_slot_index; pub(crate) use block::{ arena_cell_alloc, arena_cell_try_alloc_current, drain_block_pool_if_requested, new_object_start_bitmap, old_gen_in_use_bytes_sub, release_arena_block, @@ -47,8 +49,6 @@ pub(crate) use block::{ /// allocation path uses instead of a per-access `_tlv_get_addr`. pub(crate) use block::{arena_hot_addr, hot_arena, hot_inline_state, inline_state_hot_addr}; #[cfg(test)] -pub(crate) use block::old_gen_in_use_bytes_slot_index; -#[cfg(test)] pub(crate) use block::{ block_pool_bytes_for_test, block_pool_explicit_drained_bytes_for_test, block_pool_put, force_next_block_alloc_failure, gc_trigger_arena_borrow_depth, gc_trigger_arena_calls, diff --git a/crates/perry-runtime/src/gc/copying.rs b/crates/perry-runtime/src/gc/copying.rs index b5bb5bd577..d42daabea7 100644 --- a/crates/perry-runtime/src/gc/copying.rs +++ b/crates/perry-runtime/src/gc/copying.rs @@ -1704,8 +1704,7 @@ pub(super) fn run_copied_minor_attempt( ); } note_dirty_covered_for_presizing(dirty_scan_covered.len()); - { - } + {} let malloc_freed_bytes = if malloc_sweep_due { let phase_start = trace_phase_start(trace); let freed = sweep_malloc_objects(); diff --git a/crates/perry-runtime/src/gc/policy.rs b/crates/perry-runtime/src/gc/policy.rs index 12e089418d..73f4fc6636 100644 --- a/crates/perry-runtime/src/gc/policy.rs +++ b/crates/perry-runtime/src/gc/policy.rs @@ -2720,25 +2720,37 @@ pub(crate) fn trigger_path_hot_slot_indices() -> Vec<(&'static str, u32)> { // fast path reads" rather than "what the module declares". let _ = gc_budgeted_due_trigger(); vec![ - ("GC_OLD_RECLAIM_PENDING", GC_OLD_RECLAIM_PENDING.slot_index()), + ( + "GC_OLD_RECLAIM_PENDING", + GC_OLD_RECLAIM_PENDING.slot_index(), + ), ( "GC_LAST_OLD_RECLAIM_IN_USE_BYTES", GC_LAST_OLD_RECLAIM_IN_USE_BYTES.slot_index(), ), - ("GC_NEXT_MALLOC_TRIGGER", GC_NEXT_MALLOC_TRIGGER.slot_index()), + ( + "GC_NEXT_MALLOC_TRIGGER", + GC_NEXT_MALLOC_TRIGGER.slot_index(), + ), ("GC_NEXT_TRIGGER_BYTES", GC_NEXT_TRIGGER_BYTES.slot_index()), ("GC_TRIGGER_ARMED", GC_TRIGGER_ARMED.slot_index()), ( "GC_EXTERNAL_SIDE_LIVE_BYTES", GC_EXTERNAL_SIDE_LIVE_BYTES.slot_index(), ), - ("GC_MAJOR_PACING_RETAINING", GC_MAJOR_PACING_RETAINING.slot_index()), + ( + "GC_MAJOR_PACING_RETAINING", + GC_MAJOR_PACING_RETAINING.slot_index(), + ), ("GC_FLAGS", GC_FLAGS.slot_index()), ( "GC_BUDGETED_CYCLE_ACTIVE", GC_BUDGETED_CYCLE_ACTIVE.slot_index(), ), - ("GC_BUDGETED_STEP_ACTIVE", GC_BUDGETED_STEP_ACTIVE.slot_index()), + ( + "GC_BUDGETED_STEP_ACTIVE", + GC_BUDGETED_STEP_ACTIVE.slot_index(), + ), ( "OLD_GEN_IN_USE_BYTES", crate::arena::old_gen_in_use_bytes_slot_index(), diff --git a/crates/perry-runtime/src/gc/tests/mod.rs b/crates/perry-runtime/src/gc/tests/mod.rs index 8819176480..bac771f752 100644 --- a/crates/perry-runtime/src/gc/tests/mod.rs +++ b/crates/perry-runtime/src/gc/tests/mod.rs @@ -60,10 +60,10 @@ mod step_bounds; pub(super) mod support; mod survival_diag; mod teardown; -mod tls_fill_reentrancy; -mod trigger_path_tls; mod telemetry_verifier; mod temp_roots; +mod tls_fill_reentrancy; +mod trigger_path_tls; mod triggers; mod typed_layout_intact_residual; mod u8_inline_cache;