GC performance: 40-300x p99 pause reduction via rendezvous fix, remote batch, adaptive sweep stack
ober
2326fdce849020fb46827bab8ae0367222dbeded
new file mode 100644 --- /dev/null +++ b/docs/gc/chez-gc-findings.md @@ -0,0 +1,328 @@ +# Chez GC vs. threads vs. `(std net tcp)` — a freeze investigation + +Status: empirical findings + mechanism, written while building `jerboa-wormhole` +(a threaded WebSocket mailbox client/server that also does heavy bignum work +for a SPAKE2 PAKE). Reproduced repeatedly on macOS (Darwin 25.5, arm64, +`jerbuild`-bundled Chez). The conclusion is concrete and actionable; one part +of the *exact* internal sequence is labelled a hypothesis. + +## TL;DR + +`(std net tcp)` declares its `read`/`write`/`accept`/`connect` FFI **without** +`__collect_safe`, and works around blocking by putting the socket in +non-blocking mode and sleeping between `EAGAIN` retries. That is fine in +isolation, but it is **not safe to run concurrently with another thread that +allocates heavily** (i.e. triggers GC often). When a process contains both: + +1. one or more `(std net tcp)` reader threads sitting in their `c-read` + + `sleep` poll loop, and +2. one or more threads doing heavy allocation (here: 2048-bit modular + exponentiation for SPAKE2), + +the whole process **deadlocks** — every thread freezes, indefinitely (observed +> 60 s, i.e. forever). The fix is to declare the blocking socket syscalls +`__collect_safe` (and then they can even be plain blocking calls, no +non-blocking + sleep dance), exactly like `(std net thread-httpd)`, +`(std os aproc)` and `(std net tls-rustls)` already do. + +## The mechanism (what `__collect_safe` actually controls) + +This is documented in the stdlib itself. From `std/os/aproc.ss`: + +> Chez's built-in `(system cmd)` and `(open-process-ports ...)`'s subsequent +> I/O acquire the **TC mutex**, which means while one thread is parked in those +> calls every other green thread in the process is suspended. … blocking +> syscalls are declared `__collect_safe`, so **the TC mutex is released while +> the kernel does the work** — everything else keeps running. + +And from `std/net/tcp.ss` itself (lines 80-90), describing why it uses +non-blocking sockets: + +> a blocking `c-read` (a plain, non-`__collect_safe` foreign-procedure) then +> pins Chez's collector for the whole read, freezing every thread. + +So the model is: + +- Pure Scheme code on different OS threads **does** run in parallel (verified: + two threads each doing SPAKE2 `mod-expt` in a loop make simultaneous + progress). +- A **non-`__collect_safe` foreign call** keeps the thread "active" and holds + the **TC mutex** for the duration of the call. While it is held: + - the collector cannot complete a stop-the-world rendezvous (it needs that + thread to reach a safepoint / deactivate), and + - other threads that need to coordinate with the collector stall. +- A **`__collect_safe` foreign call** *deactivates* the thread first (releases + the TC mutex), lets the kernel block as long as it likes, and reactivates on + return. The collector treats a deactivated thread as already "parked", so GC + proceeds without it. + +`(std net tcp)` tries to dodge the "pins the collector for the whole read" +problem by never letting `c-read` block — the fd is `O_NONBLOCK`, so `c-read` +returns `EAGAIN` immediately and the thread then `(sleep ...)`s (sleep *is* +collect-safe). The intent is that each `c-read` only holds the TC mutex for a +few microseconds. + +That intent holds when GC is infrequent. It breaks when GC is frequent. + +## Why "brief, non-blocking `c-read`" is still not enough + +Under heavy allocation the collector runs *constantly*. Now consider the +reader thread's hot loop (`std/net/tcp.ss`, the custom-port `read!`): + +``` +(let retry () + (let ([n (c-read fd buf count)]) ; non-__collect_safe, holds TC mutex + (cond + [(> n 0) ...] + [(EAGAIN/EINTR) (sleep *retry-delay*) (retry)] ; sleep is collect-safe + [else 0]))) +``` + +Every ~10 ms the reader leaves the collect-safe `sleep`, **re-acquires the TC +mutex to run `c-read`**, then goes back to sleep. A heavily-allocating worker +thread, meanwhile, is asking for stop-the-world collections many times per +10 ms window. The two contend on the same TC mutex / collector handshake, and +the result is a circular wait that does not resolve: + +- the collecting thread holds the collection state and is waiting for the + reader to be deactivated; +- the reader is trying to take the TC mutex (or transition between + active `c-read` and deactivated `sleep`) and cannot, because the collector + side owns the contended state. + +(The precise instruction-level interleaving inside Chez's `S_collect` / +thread-deactivation path is a **hypothesis** — I did not read the Chez C +runtime. What is *certain* is the externally observable behaviour below and +that flipping the FFI to `__collect_safe` eliminates it.) + +The frequency matters, which is why this looked like a Heisenbug for a long +time: anything that injected extra collect-safe yields (an unbuffered file +`write()` per server command, a `(sleep)` per loop turn, even the `(std test)` +framework's own bookkeeping) changed the timing enough to *sometimes* dodge +the window. None of those are real fixes — they just lower the probability. + +## Reproductions (all on the same machine, all deterministic) + +1. **Heavy bignum, single thread** — fine. + SPAKE2 `mod-expt` (2048-bit modulus, ~2047-bit exponent) in the main + thread: completes in ~10 ms, never hangs. + +2. **Heavy bignum, two threads, NO sockets** — fine. + Two `fork-thread` workers each calling `spake2-start` 5× while the main + thread busy-waits on a shared counter: completes, exit 0. *Concurrent heavy + allocation by itself is not the problem.* + +3. **Two idle `(std net tcp)` reader threads + two heavy-bignum threads, same + process** — **freezes 5/5**. + Start the mailbox server, open two `rv-open` clients (each spawns a reader + thread that polls its idle socket), then have two threads each call + `spake2-start`. Both threads print a "starting" marker and never reach the + line after `spake2-start`. Killed at 15 s and at 60 s — a true deadlock, + not slowness. + +4. **Same as #3 but pure `mod-expt` with constant inputs (no crypto FFI at + all)** — **freezes**. + Rules out the ring crypto FFI (`jerboa_sha256`, `jerboa_random_bytes`); the + trigger is plain Chez bignum allocation, i.e. GC pressure, colliding with + the tcp reader threads. + +5. **Reducing the SPAKE2 exponent 2047→256 bits (≈8× less garbage)** — still + **freezes 5/5**. Lower GC frequency was not enough; the deadlock is a race, + not a function of total allocation. + +Marker traces from #3/#4 consistently show both worker threads stuck *inside* +`mod-expt`/`spake2-start` while the reader threads exist, confirming the two +ingredients. + +## Contrast: code that gets it right + +- `std/net/thread-httpd.ss` — thread-per-connection HTTP server. Declares + `accept`, `read`, `write` as `(foreign-procedure __collect_safe ...)` and + uses **plain blocking sockets**. Its own comment (lines 48-50): "mark them + `__collect_safe` so the calling thread is deactivated, letting other Scheme + threads run (including the accept loop while a worker is busy in read)." +- `std/os/aproc.ss` — `read`/`write`/`waitpid`/`system` all `__collect_safe`. +- `std/net/tls-rustls.ss` — `jerboa_tls_read`/`_write`/`_accept`/`_connect` + all `__collect_safe`. + +`(std net tcp)` and `std/os/fcntl.ss` are the outliers that rely on +non-blocking + sleep instead. + +## Recommended fix for `(std net tcp)` + +Declare the blocking syscalls `__collect_safe` and (optionally but +preferably) drop the non-blocking + `*retry-delay*` sleep machinery in favour +of plain blocking calls: + +```scheme +(def c-accept (foreign-procedure __collect_safe "accept" (int void* void*) int)) +(def c-connect (foreign-procedure __collect_safe "connect" (int void* int) int)) +(def c-read (foreign-procedure __collect_safe "read" (int u8* size_t) ssize_t)) +(def c-write (foreign-procedure __collect_safe "write" (int u8* size_t) ssize_t)) +``` + +Benefits: + +- No deadlock under concurrent allocation — a thread blocked in `read()` is + deactivated, so the collector never waits on it. +- Lower latency and lower CPU than the 10 ms poll loop (no busy `EAGAIN` + spinning, no minimum 10 ms wakeup granularity). +- Simpler code (the `O_NONBLOCK`, `EAGAIN`, `*retry-delay*` paths can go). + +Caveat: with blocking `accept`/`read`, `tcp-close` from another thread must +actually interrupt a blocked `read()` (closing the fd does on Linux/BSD/macOS; +the half-open / fd-reuse window already noted in `tcp.ss` still applies and is +worth a follow-up). + +## Workarounds for callers who can't patch the stdlib + +If you must keep `(std net tcp)`: + +- Don't run heavy-allocation Scheme on one thread while another thread holds a + `(std net tcp)` reader in the same process. In practice that means: do the + expensive crypto **before** spawning the socket reader thread, or run the + socket peer in a **separate process**. +- Or ship a private GC-safe TCP shim (a ~120-line copy of `tcp.ss`'s FFI with + `__collect_safe` on read/write/accept/connect). This is what `jerboa-wormhole` + does in `wormhole/net.ss`. + +## Second front: `condition-wait` / `mutex-acquire` are not collect-safe either + +Fixing the socket layer was necessary but **not sufficient**. With GC-safe +sockets, a second freeze remained whenever **two threads in one process both +drove socket I/O while sharing mutex-guarded state**. Concretely: a rendezvous +client with a background reader thread (filling a queue under a mutex) plus a +caller thread waiting on that queue. Two such clients in one process froze +~deterministically, even with *zero* heavy allocation (no crypto, no bignum) — +the JSON churn of the relay handshake was enough GC pressure. + +The same TC-mutex rule applies to Chez's own blocking thread primitives: + +- A thread parked in **`condition-wait`** holds/▸needs the TC coordination + state for the whole wait; if another thread triggers GC it cannot complete + the stop-the-world rendezvous → freeze. +- A thread blocked in **`mutex-acquire`** on a *contended* mutex is "active" + and not at a safepoint; a GC during that block waits for it, and if the + holder is itself waiting on the collector you get a circular wait. + +`(sleep ...)` poll loops were tried as a "GC-safe" substitute for +`condition-wait`. They lowered the rate but did **not** eliminate the freeze, +because the brief `with-mutex` critical sections (reader vs. waiter on the same +queue mutex) are still non-collect-safe windows, and two concurrent clients hit +them often enough. + +What actually fixed it: **remove the cross-thread coordination entirely.** The +mailbox protocol is request/response, so the client was rewritten to be +single-threaded — each "receive" is an inline blocking read on the (now +collect-safe) socket, buffering any unrelated frames in a thread-local list. +No reader thread, no queue mutex, no condition variable. Each connection is +owned by exactly one thread. With that plus the GC-safe socket layer, two +peers doing a full SPAKE2 exchange concurrently in one process run 6/6, 10/10, +20/20. + +Design rule that falls out of this: + +> In threaded Chez with this GC, treat **every** non-`__collect_safe` blocking +> primitive — foreign syscalls, `condition-wait`, contended `mutex-acquire` — +> as something that can wedge a stop-the-world collection. Prefer designs where +> each socket/resource is owned by a single thread and threads do **not** block +> on shared synchronization while other threads allocate. When you must block, +> block in a `__collect_safe` call. + +For the relay the same rule shows up as a *write* problem: a broadcast writes +to other peers' sockets, so two handler threads can contend on one socket's +write mutex — and if the holder is parked in the `__collect_safe` send while the +other waits on the mutex, GC deadlocks. The fix is the dual of the client's: +give each connection a single **writer thread** fed by a (wormhole queue), so +each socket's write mutex is only ever taken by one thread and never contended. +(It hid for a while because the relay allocates lightly and runs in its own +process; merely importing another module shifted the timing enough to make an +all-in-one-process test deadlock every run.) + +## Third front: even FAST foreign calls — `(std crypto native-rust)` + +The freeze is not specific to *blocking* syscalls. `(std crypto native-rust)`'s +ring bindings (`jerboa_sha256`, `jerboa_hmac_sha256`, `jerboa_chacha20_*`, +`jerboa_random_bytes`, …) are short, non-blocking CPU functions — and they are +declared **without** `__collect_safe`. They still wedge GC: each call holds the +TC mutex for its (brief) duration, and when two threads make *many* such calls +while allocating (a SPAKE2 PAKE: random → sha256 → bignum → chacha20/hmac for +the key-confirmation phase, ×2 peers), the collector keeps trying to stop the +world and keeps finding a thread mid-FFI. Empirically: a single `random-bytes` +per peer was fine; the full crypto sequence froze ~deterministically. + +Marking those FFIs `__collect_safe` fixes it (a private GC-safe copy of the +needed bindings lives in `jerboa-wormhole/wormhole/crypto-native.ss`). The +deactivate/reactivate cost is negligible for short calls. + +Recommendation: `(std crypto native-rust)` should declare its +`foreign-procedure`s `__collect_safe` (or ship a `*-safe` variant). The ring +calls are pure and operate only on the passed bytevectors, so they are safe to +run while the collector moves *other* objects; Chez locks the `u8*` bytevector +arguments for the duration of the call (the same reason `(std net thread-httpd)` +can pass bytevectors straight to a `__collect_safe` `read`/`write`). + +Generalised rule: in threaded Chez, **any** non-`__collect_safe` foreign call — +blocking or not — that runs concurrently with allocating threads is a latent +freeze. If a foreign binding will ever be called from a worker thread in a +multi-threaded process, declare it `__collect_safe`. + +## Why this was painful to localise + +- Output is lost on `SIGTERM`/timeout kill because Chez block-buffers ports + when stdout/stderr is a pipe; only **unbuffered file ports** + (`(buffer-mode none)`) survived the kill and revealed where each thread + stopped. +- The `(std test)` framework wraps each case body in nested `guard`s and does + its own allocation; that perturbed GC timing enough to flip the bug on/off, + which falsely implicated the test framework. Networked, multi-thread tests + are more reliable as plain scripts. +- Sub-millisecond `(sleep ...)` "yields" do not reliably deactivate the thread + the way a real blocking/collect-safe call does, so they are not a fix. + +## Follow-up: collect-safe is necessary but not sufficient — heavy-allocation co-tenants + +A later finding from `jerboa-wormhole`'s transit relay. Every blocking syscall +in play was already `__collect_safe` (sockets via `wormhole/net.ss`, ring crypto +via `wormhole/crypto-native.ss`), and `(sleep ...)` is collect-safe — yet a +*second* server sharing the process still destabilised the first. + +The mailbox relay (a latency-sensitive WebSocket request/response server) and +the transit relay (a throughput server that pairs two sockets and pipes the bulk +file bytes between them) were started in the **same process** by `wormhole +relay`. Measured byte-identical file-transfer success over a localhost relay: + +| configuration | success | +|------------------------------------------------|---------| +| direct transit, no relay involved | 20/20 | +| relay path, relay **co-hosted** with mailbox | ~6/10 | +| relay path, relay in a **separate** process | ~9/10 | +| relay-only (whole file piped), **co-hosted** | ~7/10 | + +The failure is always the *mailbox* connection dropping ("connection closed +waiting for phase …") at an unrelated moment — not the transit connection. The +mechanism: the transit relay's pipe loop allocates a fresh bytevector per chunk +(`get-bytevector-some`), so piping a multi-hundred-KB file is a burst of +allocation. That allocation triggers GC *in the shared heap*, and a mailbox +reader thread momentarily between collect-safe points (e.g. parsing a frame, +mid-`ws-recv`) makes the stop-the-world pause long enough that a peer read +returns short; `ws-recv` treats any read glitch as EOF and closes the socket. + +So: **collect-safe FFI keeps a single subsystem from deadlocking, but it does +not isolate two subsystems that share a heap.** A high-allocation-rate worker +(a byte pump, a compressor, a bulk hasher) co-resident with a +latency-sensitive request/response server will inject GC pauses into the latter. +Two robust options: + +1. **Process isolation** — run the throughput server as its own OS process so it + has its own heap and its GC pauses can't touch the latency-sensitive one. + This is what `jerboa-wormhole` settled on: `wormhole relay` is mailbox-only, + the transit relay is a separate `wormhole transit-relay`, and it is opt-in. +2. **Allocation discipline in the hot path** — reuse a single buffer for the + pipe instead of allocating per chunk, to cut the GC trigger rate. Helps, but + does not fully decouple the two subsystems' GC the way (1) does. + +Forking the throughput child *early* (before any threads exist) is the cheap way +to get (1) from one launcher; forking a process that already has worker threads +is unsafe (only the forking thread survives, and any lock another thread held is +frozen in the child). new file mode 100644 --- /dev/null +++ b/docs/gc/data/baseline-index.csv @@ -0,0 +1,2 @@ +workload,threads,collect_trip_bytes,sweeper_cap,sweeper_min_work,assignment_policy,dirty_assignment,remote_batch,descriptor_experiment,region_experiment,mark_experiment,json_file,stdout_file,stderr_file,metrics_file +region-update-edges,4,32768,default,default,default,default,default,default,evacuate,default,/Users/user/mine/jerboa-gc/build/gc-baseline/region-update-edges-4-trip-32768-cap-default-minwork-default-assign-default-dirty-default-remote-default-desc-default-region-evacuate-mark-default.jsonl,/Users/user/mine/jerboa-gc/build/gc-baseline/region-update-edges-4-trip-32768-cap-default-minwork-default-assign-default-dirty-default-remote-default-desc-default-region-evacuate-mark-default.out,/Users/user/mine/jerboa-gc/build/gc-baseline/region-update-edges-4-trip-32768-cap-default-minwork-default-assign-default-dirty-default-remote-default-desc-default-region-evacuate-mark-default.err,/Users/user/mine/jerboa-gc/build/gc-baseline/region-update-edges-4-trip-32768-cap-default-minwork-default-assign-default-dirty-default-remote-default-desc-default-region-evacuate-mark-default.metrics new file mode 100644 --- /dev/null +++ b/docs/gc/data/baseline-summary.csv @@ -0,0 +1,2 @@ +workload,threads,collect_trip_bytes,sweeper_cap,sweeper_min_work,assignment_policy,dirty_assignment,remote_batch,descriptor_experiment,region_experiment,mark_experiment,samples,time_mode,real_seconds,user_seconds,sys_seconds,cpu_pct,max_rss_kb,iterations_per_sec,result,gc_par_samples,gc_011_samples,gc_ocd_samples,gc_oce_samples,copy_source_segments_total,mark_source_segments_total,copy_source_bytes_total,mark_source_bytes_total,max_copy_source_segments,max_mark_source_segments,max_descriptor_experiment,descriptor_source_segments_total,descriptor_source_segments_max,descriptor_max_owner_segments_max,descriptor_source_bytes_total,descriptor_source_bytes_max,descriptor_max_owner_bytes_max,descriptor_split_segments_total,descriptor_split_bytes_total,max_descriptor_split_segments,max_descriptor_split_bytes,max_region_experiment,region_barrier_stores_total,region_cross_segment_stores_total,region_cross_generation_stores_total,region_old_to_young_stores_total,region_old_to_old_cross_segment_stores_total,region_distinct_edges_total,max_region_distinct_edges,region_edge_table_overflow_total,max_region_selection_budget_bytes,max_region_candidate_segments,region_candidate_bytes_total,region_candidate_live_bytes_total,region_candidate_scan_cost_bytes_total,region_candidate_evacuation_cost_bytes_total,max_region_selected_segments,region_selected_bytes_total,region_selected_live_bytes_total,region_selected_scan_cost_bytes_total,region_selected_evacuation_cost_bytes_total,max_region_excluded_segments,max_region_evacuation_experiment,max_region_evacuation_selected_segments,max_region_evacuation_eligible_segments,max_region_evacuation_blocked_segments,region_evacuation_inbound_refs_total,region_evacuation_internal_refs_total,region_evacuation_outbound_refs_total,region_evacuation_update_refs_total,region_evacuation_table_overflow_total,region_evacuation_required_bytes_total,region_evacuation_reserve_bytes_max,region_evacuation_deficit_bytes_total,region_evacuation_reserve_failures_total,max_region_relocation_plan_source_segments,max_region_relocation_plan_target_segments,region_relocation_plan_copy_bytes_total,region_relocation_plan_update_refs_total,region_relocation_plan_metadata_bytes_total,region_relocation_plan_failures_total,max_region_relocation_target_available_segments,region_relocation_target_available_bytes_max,max_region_relocation_target_sampled_segments,max_region_relocation_target_reserved_segments,region_relocation_target_reserved_bytes_total,region_relocation_target_inventory_failures_total,region_relocation_target_reservation_failures_total,region_relocation_target_unreserve_failures_total,max_region_relocation_allocator_segments,region_relocation_allocator_capacity_bytes_total,region_relocation_allocator_used_bytes_total,region_relocation_allocator_waste_bytes_total,max_region_relocation_allocator_cursor_bytes,region_relocation_allocator_failures_total,max_region_relocation_map_entries,region_relocation_map_bytes_total,max_region_relocation_map_hash,max_region_relocation_map_source_segments,max_region_relocation_map_target_segments,region_relocation_map_failures_total,max_region_relocation_object_preflight_segments,region_relocation_object_preflight_objects_total,region_relocation_object_preflight_bytes_total,region_relocation_object_preflight_excluded_segments_total,region_relocation_object_preflight_excluded_bytes_total,region_relocation_object_preflight_failures_total,region_relocation_copy_schedule_objects_total,region_relocation_copy_schedule_bytes_total,max_region_relocation_copy_schedule_target_segments,max_region_relocation_copy_schedule_cursor_bytes,region_relocation_copy_schedule_waste_bytes_total,region_relocation_copy_schedule_failures_total,region_relocation_copy_entry_objects_total,region_relocation_copy_entry_bytes_total,region_relocation_copy_entry_metadata_bytes_total,max_region_relocation_copy_entry_hash,region_relocation_copy_entry_failures_total,region_relocation_copy_bytes_objects_total,region_relocation_copy_bytes_copied_total,max_region_relocation_copy_bytes_checksum,region_relocation_copy_bytes_failures_total,region_relocation_forwarding_entries_total,region_relocation_forwarding_bytes_total,region_relocation_forwarding_metadata_bytes_total,max_region_relocation_forwarding_hash,region_relocation_forwarding_failures_total,region_relocation_rewrite_edges_total,region_relocation_rewrite_candidates_total,region_relocation_rewrite_forwarding_hits_total,region_relocation_rewrite_metadata_bytes_total,max_region_relocation_rewrite_hash,region_relocation_rewrite_failures_total,region_relocation_slot_rewrite_objects_total,region_relocation_slot_rewrite_slots_total,region_relocation_slot_rewrite_selected_slots_total,region_relocation_slot_rewrite_forwarding_hits_total,region_relocation_slot_rewrite_deferred_objects_total,region_relocation_slot_rewrite_deferred_bytes_total,region_relocation_slot_rewrite_metadata_bytes_total,max_region_relocation_slot_rewrite_hash,region_relocation_slot_rewrite_failures_total,region_relocation_copy_slot_rewrite_slots_total,region_relocation_copy_slot_rewrite_selected_slots_total,region_relocation_copy_slot_rewrite_applied_slots_total,region_relocation_copy_slot_rewrite_deferred_slots_total,region_relocation_copy_slot_rewrite_metadata_bytes_total,max_region_relocation_copy_slot_rewrite_hash,region_relocation_copy_slot_rewrite_failures_total,region_relocation_target_publish_segments_total,region_relocation_target_publish_bytes_total,region_relocation_target_publish_objects_total,region_relocation_target_publish_forwarding_entries_total,region_relocation_target_publish_metadata_bytes_total,max_region_relocation_target_publish_hash,region_relocation_target_publish_failures_total,region_relocation_external_slot_rewrite_cards_total,region_relocation_external_slot_rewrite_slots_total,region_relocation_external_slot_rewrite_selected_slots_total,region_relocation_external_slot_rewrite_forwarding_hits_total,region_relocation_external_slot_rewrite_deferred_cards_total,region_relocation_external_slot_rewrite_metadata_bytes_total,max_region_relocation_external_slot_rewrite_hash,region_relocation_external_slot_rewrite_failures_total,max_mark_experiment,satb_prewrite_stores_total,satb_old_heap_refs_total,satb_old_nonstatic_refs_total,satb_old_static_refs_total,satb_old_to_younger_refs_total,satb_new_heap_refs_total,satb_queue_candidates_total,satb_queue_enqueued_total,satb_queue_overflow_total,satb_queue_drained_total,max_satb_queue_capacity,satb_thread_buffered_total,satb_thread_buffer_flushes_total,satb_thread_buffer_overflow_total,max_satb_thread_buffer_capacity,max_satb_thread_buffer_max_fill,max_satb_mark_epoch,satb_concurrent_mark_starts_total,satb_concurrent_mark_finishes_total,satb_concurrent_mark_drained_total,satb_concurrent_mark_fallbacks_total,satb_remark_drained_total,satb_shadow_marked_total,satb_shadow_duplicates_total,satb_shadow_overflow_total,satb_mark_bitmap_segments_total,satb_mark_bitmap_bytes_total,satb_mark_bitmap_apply_segments_total,satb_mark_bitmap_apply_bits_total,satb_mark_bitmap_apply_old_space_bits_total,satb_mark_bitmap_apply_mark_mode_bits_total,satb_mark_bitmap_apply_copy_mode_bits_total,satb_mark_bitmap_apply_extended_old_space_bits_total,satb_mark_bitmap_apply_extended_mark_mode_bits_total,satb_mark_bitmap_apply_extended_copy_mode_bits_total,satb_mark_bitmap_apply_extended_skipped_bits_total,satb_mark_bitmap_apply_skipped_bits_total,satb_mark_bitmap_real_apply_objects_total,satb_mark_bitmap_real_apply_extended_objects_total,satb_mark_bitmap_real_apply_interior_bits_total,satb_mark_bitmap_real_apply_skipped_bits_total,satb_mark_bitmap_real_apply_failures_total,satb_mark_bitmap_copy_reconcile_objects_total,satb_mark_bitmap_copy_reconcile_extended_objects_total,satb_mark_bitmap_copy_reconcile_forwarded_total,satb_mark_bitmap_copy_reconcile_not_forwarded_total,satb_mark_bitmap_copy_reconcile_interior_bits_total,satb_mark_bitmap_copy_reconcile_skipped_bits_total,satb_mark_bitmap_copy_reconcile_failures_total,satb_generated_prewrite_records_total,satb_dirty_card_records_total,satb_dirty_card_heap_refs_total,satb_dirty_card_younger_refs_total,max_aux_sweepers,max_requested_aux_sweepers,max_effective_aux_sweepers,max_sweeper_estimated_work_bytes,max_sweeper_min_work_bytes,max_greedy_assignment,max_remote_consecutive_batch,max_dirty_greedy_assignment,dirty_rebalanced_segments_total,dirty_rebalanced_bytes_total,max_worker_count,max_workers_with_swept_bytes,max_worker_wall_us,max_worker_accum_us,max_worker_step_us,max_worker_swept_bytes,remote_flushes_total,remote_delivery_batches_total,remote_lock_wait_total_us,remote_lock_hold_total_us,max_remote_send_batch,max_remote_delivery_batch,max_remote_receive_depth,remote_sent_total,remote_received_total,request_to_start_min_us,request_to_start_p50_us,request_to_start_p95_us,request_to_start_p99_us,request_to_start_p999_us,request_to_start_avg_us,request_to_start_max_us,collector_min_us,collector_p50_us,collector_p95_us,collector_p99_us,collector_p999_us,collector_avg_us,collector_max_us,setup_p99_us,mark_sweep_p99_us,special_p99_us,rebuild_p99_us,finish_p99_us,dirty_setup_p99_us,guardian_finalizer_p99_us,weak_pair_p99_us,ephemeron_p99_us +region-update-edges,4,32768,default,default,default,default,default,default,evacuate,default,0,bsd,0.260000,0.050000,0.010000,23.08,54448,3846.15,1024,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 new file mode 100644 --- /dev/null +++ b/docs/gc/data/mats-summary.csv @@ -0,0 +1,7 @@ +build,target,mode,status,log,telemetry_file +chez-gc-default_,thread.mo,off,pass,/Users/user/mine/jerboa-gc/build/gc-mats/chez-gc-default_-thread.mo-off.log, +chez-gc-default_,foreign.mo,off,pass,/Users/user/mine/jerboa-gc/build/gc-mats/chez-gc-default_-foreign.mo-off.log, +chez-gc-telemetry_,thread.mo,off,pass,/Users/user/mine/jerboa-gc/build/gc-mats/chez-gc-telemetry_-thread.mo-off.log, +chez-gc-telemetry_,thread.mo,on,pass,/Users/user/mine/jerboa-gc/build/gc-mats/chez-gc-telemetry_-thread.mo-on.log,/Users/user/mine/jerboa-gc/build/gc-mats/chez-gc-telemetry_-thread.mo-on.jsonl +chez-gc-telemetry_,foreign.mo,off,pass,/Users/user/mine/jerboa-gc/build/gc-mats/chez-gc-telemetry_-foreign.mo-off.log, +chez-gc-telemetry_,foreign.mo,on,pass,/Users/user/mine/jerboa-gc/build/gc-mats/chez-gc-telemetry_-foreign.mo-on.log,/Users/user/mine/jerboa-gc/build/gc-mats/chez-gc-telemetry_-foreign.mo-on.jsonl new file mode 100644 --- /dev/null +++ b/docs/gc/data/qualification-summary.csv @@ -0,0 +1,14 @@ +step,status,log +gc-ffi-audit,pass,/Users/user/mine/jerboa-gc/build/gc-qualification-smoke-current-1784332314/gc-ffi-audit.log +data-check,pass,/Users/user/mine/jerboa-gc/build/gc-qualification-smoke-current-1784332314/data-check.log +check-docs,pass,/Users/user/mine/jerboa-gc/build/gc-qualification-smoke-current-1784332314/check-docs.log +gc-rendezvous-watchdog,pass,/Users/user/mine/jerboa-gc/build/gc-qualification-smoke-current-1784332314/gc-rendezvous-watchdog.log +gc-shadow-reachability,pass,/Users/user/mine/jerboa-gc/build/gc-qualification-smoke-current-1784332314/gc-shadow-reachability.log +gc-correctness-stress,pass,/Users/user/mine/jerboa-gc/build/gc-qualification-smoke-current-1784332314/gc-correctness-stress.log +gc-special-stress,pass,/Users/user/mine/jerboa-gc/build/gc-qualification-smoke-current-1784332314/gc-special-stress.log +gc-baseline,pass,/Users/user/mine/jerboa-gc/build/gc-qualification-smoke-current-1784332314/gc-baseline.log +gc-baseline-summary,pass,/Users/user/mine/jerboa-gc/build/gc-qualification-smoke-current-1784332314/gc-baseline-summary.log +gc-baseline-compare,pass,/Users/user/mine/jerboa-gc/build/gc-qualification-smoke-current-1784332314/gc-baseline-compare.log +gc-pause-attribution,pass,/Users/user/mine/jerboa-gc/build/gc-qualification-smoke-current-1784332314/gc-pause-attribution.log +gc-throughput,pass,/Users/user/mine/jerboa-gc/build/gc-qualification-smoke-current-1784332314/gc-throughput.log +gc-mats,pass,/Users/user/mine/jerboa-gc/build/gc-qualification-smoke-current-1784332314/gc-mats.log new file mode 100644 --- /dev/null +++ b/docs/gc/data/watchdog/blocking-io-summary.csv @@ -0,0 +1,2 @@ +workload,threads,collect_trip_bytes,sweeper_cap,sweeper_min_work,assignment_policy,dirty_assignment,remote_batch,descriptor_experiment,region_experiment,mark_experiment,samples,time_mode,real_seconds,user_seconds,sys_seconds,cpu_pct,max_rss_kb,iterations_per_sec,result,gc_par_samples,gc_011_samples,gc_ocd_samples,gc_oce_samples,copy_source_segments_total,mark_source_segments_total,copy_source_bytes_total,mark_source_bytes_total,max_copy_source_segments,max_mark_source_segments,max_descriptor_experiment,descriptor_source_segments_total,descriptor_source_segments_max,descriptor_max_owner_segments_max,descriptor_source_bytes_total,descriptor_source_bytes_max,descriptor_max_owner_bytes_max,descriptor_split_segments_total,descriptor_split_bytes_total,max_descriptor_split_segments,max_descriptor_split_bytes,max_region_experiment,region_barrier_stores_total,region_cross_segment_stores_total,region_cross_generation_stores_total,region_old_to_young_stores_total,region_old_to_old_cross_segment_stores_total,region_distinct_edges_total,max_region_distinct_edges,region_edge_table_overflow_total,max_region_selection_budget_bytes,max_region_candidate_segments,region_candidate_bytes_total,region_candidate_live_bytes_total,region_candidate_scan_cost_bytes_total,region_candidate_evacuation_cost_bytes_total,max_region_selected_segments,region_selected_bytes_total,region_selected_live_bytes_total,region_selected_scan_cost_bytes_total,region_selected_evacuation_cost_bytes_total,max_region_excluded_segments,max_region_excluded_mark_segments,max_region_excluded_special_segments,max_region_excluded_space_segments,max_region_excluded_layout_segments,max_region_typed_candidate_segments,max_region_typed_selected_segments,max_region_typed_layout_excluded_segments,max_region_root_candidate_segments,max_region_root_selected_segments,max_region_root_layout_excluded_segments,max_region_evacuation_experiment,max_region_evacuation_selected_segments,max_region_evacuation_eligible_segments,max_region_evacuation_blocked_segments,region_evacuation_inbound_refs_total,region_evacuation_internal_refs_total,region_evacuation_outbound_refs_total,region_evacuation_update_refs_total,region_evacuation_table_overflow_total,region_evacuation_required_bytes_total,region_evacuation_reserve_bytes_max,region_evacuation_deficit_bytes_total,region_evacuation_reserve_failures_total,region_evacuation_failure_policy_events_total,region_evacuation_failure_policy_reserve_ok_events_total,region_evacuation_failure_policy_fallback_events_total,region_evacuation_failure_policy_deficit_bytes_total,region_evacuation_failure_policy_failures_total,region_evacuation_fallback_suppressed_events_total,region_evacuation_fallback_suppressed_source_segments_total,region_evacuation_fallback_suppressed_target_segments_total,region_evacuation_fallback_suppressed_copy_bytes_total,region_evacuation_fallback_suppressed_failures_total,region_evacuation_shrink_attempt_events_total,region_evacuation_shrink_success_events_total,region_evacuation_shrink_fallback_events_total,max_region_evacuation_shrink_selected_segments,max_region_evacuation_shrink_target_segments,region_evacuation_shrink_copy_bytes_total,region_evacuation_shrink_deficit_bytes_total,region_evacuation_shrink_failures_total,max_region_relocation_plan_source_segments,max_region_relocation_plan_target_segments,region_relocation_plan_copy_bytes_total,region_relocation_plan_update_refs_total,region_relocation_plan_metadata_bytes_total,region_relocation_plan_failures_total,max_region_relocation_target_available_segments,region_relocation_target_available_bytes_max,max_region_relocation_target_sampled_segments,max_region_relocation_target_reserved_segments,region_relocation_target_reserved_bytes_total,region_relocation_target_inventory_failures_total,region_relocation_target_reservation_failures_total,region_relocation_target_unreserve_failures_total,max_region_relocation_allocator_segments,region_relocation_allocator_capacity_bytes_total,region_relocation_allocator_used_bytes_total,region_relocation_allocator_waste_bytes_total,max_region_relocation_allocator_cursor_bytes,region_relocation_allocator_failures_total,max_region_relocation_map_entries,region_relocation_map_bytes_total,max_region_relocation_map_hash,max_region_relocation_map_source_segments,max_region_relocation_map_target_segments,region_relocation_map_failures_total,max_region_relocation_object_preflight_segments,region_relocation_object_preflight_objects_total,region_relocation_object_preflight_bytes_total,region_relocation_object_preflight_excluded_segments_total,region_relocation_object_preflight_excluded_bytes_total,region_relocation_object_preflight_failures_total,region_relocation_copy_schedule_objects_total,region_relocation_copy_schedule_bytes_total,max_region_relocation_copy_schedule_target_segments,max_region_relocation_copy_schedule_cursor_bytes,region_relocation_copy_schedule_waste_bytes_total,region_relocation_copy_schedule_failures_total,region_relocation_copy_entry_objects_total,region_relocation_copy_entry_bytes_total,region_relocation_copy_entry_metadata_bytes_total,max_region_relocation_copy_entry_hash,region_relocation_copy_entry_failures_total,region_relocation_copy_bytes_objects_total,region_relocation_copy_bytes_copied_total,max_region_relocation_copy_bytes_checksum,region_relocation_copy_bytes_failures_total,region_relocation_forwarding_entries_total,region_relocation_forwarding_bytes_total,region_relocation_forwarding_metadata_bytes_total,max_region_relocation_forwarding_hash,region_relocation_forwarding_failures_total,region_relocation_forwarding_marker_objects_total,region_relocation_forwarding_marker_bytes_total,region_relocation_forwarding_marker_installed_objects_total,region_relocation_forwarding_marker_restored_objects_total,region_relocation_forwarding_marker_pair_objects_total,region_relocation_forwarding_marker_symbol_objects_total,region_relocation_forwarding_marker_vector_objects_total,region_relocation_forwarding_marker_stencil_vector_objects_total,region_relocation_forwarding_marker_box_objects_total,region_relocation_forwarding_marker_tlc_objects_total,region_relocation_forwarding_marker_ratnum_objects_total,region_relocation_forwarding_marker_exactnum_objects_total,max_region_relocation_forwarding_marker_hash,region_relocation_forwarding_marker_failures_total,region_relocation_rewrite_edges_total,region_relocation_rewrite_candidates_total,region_relocation_rewrite_forwarding_hits_total,region_relocation_rewrite_metadata_bytes_total,max_region_relocation_rewrite_hash,region_relocation_rewrite_failures_total,region_relocation_slot_rewrite_objects_total,region_relocation_slot_rewrite_slots_total,region_relocation_slot_rewrite_selected_slots_total,region_relocation_slot_rewrite_forwarding_hits_total,region_relocation_slot_rewrite_deferred_objects_total,region_relocation_slot_rewrite_deferred_bytes_total,region_relocation_slot_rewrite_deferred_typed_objects_total,region_relocation_slot_rewrite_deferred_space_objects_total,region_relocation_slot_rewrite_metadata_bytes_total,max_region_relocation_slot_rewrite_hash,region_relocation_slot_rewrite_failures_total,region_relocation_copy_slot_rewrite_slots_total,region_relocation_copy_slot_rewrite_selected_slots_total,region_relocation_copy_slot_rewrite_applied_slots_total,region_relocation_copy_slot_rewrite_deferred_slots_total,region_relocation_copy_slot_rewrite_metadata_bytes_total,max_region_relocation_copy_slot_rewrite_hash,region_relocation_copy_slot_rewrite_failures_total,region_relocation_target_publish_segments_total,region_relocation_target_publish_bytes_total,region_relocation_target_publish_objects_total,region_relocation_target_publish_forwarding_entries_total,region_relocation_target_publish_metadata_bytes_total,max_region_relocation_target_publish_hash,region_relocation_target_publish_failures_total,region_relocation_target_link_segments_total,region_relocation_target_link_bytes_total,region_relocation_target_linked_segments_total,region_relocation_target_unlinked_segments_total,region_relocation_target_link_accounting_bytes_total,max_region_relocation_target_link_hash,region_relocation_target_link_failures_total,region_relocation_external_slot_rewrite_cards_total,region_relocation_external_slot_rewrite_slots_total,region_relocation_external_slot_rewrite_selected_slots_total,region_relocation_external_slot_rewrite_forwarding_hits_total,region_relocation_external_slot_rewrite_deferred_cards_total,region_relocation_external_slot_rewrite_deferred_marked_cards_total,region_relocation_external_slot_rewrite_deferred_ambiguous_cards_total,region_relocation_external_slot_rewrite_deferred_unsupported_cards_total,region_relocation_external_slot_rewrite_deferred_special_cards_total,region_relocation_external_slot_rewrite_metadata_bytes_total,max_region_relocation_external_slot_rewrite_hash,region_relocation_external_slot_rewrite_failures_total,region_relocation_live_slot_rewrite_selected_slots_total,region_relocation_live_slot_rewrite_applied_slots_total,region_relocation_live_slot_rewrite_restored_slots_total,region_relocation_live_external_slot_rewrite_selected_slots_total,region_relocation_live_external_slot_rewrite_applied_slots_total,region_relocation_live_external_slot_rewrite_restored_slots_total,region_relocation_live_slot_rewrite_commit_ordered_slots_total,region_relocation_live_external_slot_rewrite_commit_ordered_slots_total,max_region_relocation_live_slot_rewrite_hash,region_relocation_live_slot_rewrite_failures_total,region_relocation_one_way_rewrite_selected_slots_total,region_relocation_one_way_rewrite_applied_slots_total,region_relocation_one_way_rewrite_verified_slots_total,region_relocation_one_way_rewrite_restored_slots_total,region_relocation_one_way_external_rewrite_selected_slots_total,region_relocation_one_way_external_rewrite_applied_slots_total,region_relocation_one_way_external_rewrite_verified_slots_total,region_relocation_one_way_external_rewrite_restored_slots_total,region_relocation_one_way_rewrite_prerequisite_failures_total,max_region_relocation_one_way_rewrite_hash,region_relocation_one_way_rewrite_failures_total,region_relocation_source_retire_segments_total,region_relocation_source_retire_bytes_total,region_relocation_source_retire_forwarded_objects_total,region_relocation_source_retire_target_segments_total,region_relocation_source_retire_commit_ordered_slots_total,max_region_relocation_source_retire_hash,region_relocation_source_retire_failures_total,region_relocation_source_retire_proof_segments_total,region_relocation_source_retire_proof_bytes_total,region_relocation_source_retire_proof_emptied_segments_total,region_relocation_source_retire_proof_restored_segments_total,region_relocation_source_retire_proof_nonstatic_delta_segments_total,region_relocation_source_retire_proof_empty_delta_segments_total,region_relocation_source_retire_proof_chunk_delta_segments_total,region_relocation_source_retire_proof_heap_delta_bytes_total,max_region_relocation_source_retire_proof_hash,region_relocation_source_retire_proof_failures_total,region_relocation_source_live_emit_requested_events_total,region_relocation_source_live_emit_ready_events_total,region_relocation_source_live_emit_emptied_segments_total,region_relocation_source_live_emit_bytes_total,region_relocation_source_live_emit_nonstatic_delta_segments_total,region_relocation_source_live_emit_empty_delta_segments_total,region_relocation_source_live_emit_chunk_delta_segments_total,region_relocation_source_live_emit_heap_delta_bytes_total,region_relocation_source_live_emit_cleanup_restored_segments_total,region_relocation_source_live_emit_cleanup_bytes_total,region_relocation_source_live_emit_prerequisite_failures_total,max_region_relocation_source_live_emit_hash,region_relocation_source_live_emit_failures_total,region_relocation_source_sweep_accounting_requested_events_total,region_relocation_source_sweep_accounting_ready_events_total,region_relocation_source_sweep_accounting_retired_segments_total,region_relocation_source_sweep_accounting_retired_bytes_total,region_relocation_source_sweep_accounting_nonstatic_delta_segments_total,region_relocation_source_sweep_accounting_empty_delta_segments_total,region_relocation_source_sweep_accounting_chunk_delta_segments_total,region_relocation_source_sweep_accounting_heap_delta_bytes_total,region_relocation_source_sweep_accounting_restore_cleanup_required_events_total,region_relocation_source_sweep_accounting_sweep_integration_missing_events_total,region_relocation_source_sweep_accounting_prerequisite_failures_total,max_region_relocation_source_sweep_accounting_hash,region_relocation_source_sweep_accounting_failures_total,region_relocation_target_retention_requested_events_total,region_relocation_target_retention_ready_events_total,region_relocation_target_retention_blocked_events_total,region_relocation_target_retention_missing_prerequisites_total,region_relocation_target_retention_release_blockers_total,region_relocation_target_retention_unlink_blockers_total,region_relocation_target_retention_accounting_blockers_total,max_region_relocation_target_retention_blocker_mask,region_relocation_target_retention_failures_total,region_relocation_target_retain_proof_segments_total,region_relocation_target_retain_proof_bytes_total,region_relocation_target_retain_proof_linked_segments_total,region_relocation_target_retain_proof_unlinked_segments_total,region_relocation_target_retain_proof_accounting_bytes_total,region_relocation_target_retain_proof_heap_delta_bytes_total,max_region_relocation_target_retain_proof_hash,region_relocation_target_retain_proof_failures_total,region_relocation_target_release_suppression_requested_events_total,region_relocation_target_release_suppression_ready_events_total,region_relocation_target_release_suppression_suppressed_segments_total,region_relocation_target_release_suppression_suppressed_bytes_total,region_relocation_target_release_suppression_retained_segments_total,region_relocation_target_release_suppression_retained_bytes_total,region_relocation_target_release_suppression_cleanup_segments_total,region_relocation_target_release_suppression_cleanup_bytes_total,region_relocation_target_release_suppression_prerequisite_failures_total,max_region_relocation_target_release_suppression_hash,region_relocation_target_release_suppression_failures_total,region_relocation_target_live_emit_requested_events_total,region_relocation_target_live_emit_ready_events_total,region_relocation_target_live_emit_linked_segments_total,region_relocation_target_live_emit_linked_bytes_total,region_relocation_target_live_emit_heap_delta_bytes_total,region_relocation_target_live_emit_cleanup_unlinked_segments_total,region_relocation_target_live_emit_cleanup_released_segments_total,region_relocation_target_live_emit_cleanup_bytes_total,region_relocation_target_live_emit_prerequisite_failures_total,max_region_relocation_target_live_emit_hash,region_relocation_target_live_emit_failures_total,region_relocation_slot_live_emit_selected_slots_total,region_relocation_slot_live_emit_applied_slots_total,region_relocation_slot_live_emit_verified_slots_total,region_relocation_slot_live_emit_restored_slots_total,region_relocation_slot_live_emit_external_selected_slots_total,region_relocation_slot_live_emit_external_applied_slots_total,region_relocation_slot_live_emit_external_verified_slots_total,region_relocation_slot_live_emit_external_restored_slots_total,region_relocation_slot_live_emit_prerequisite_failures_total,max_region_relocation_slot_live_emit_hash,region_relocation_slot_live_emit_failures_total,region_relocation_root_rewrite_requested_events_total,region_relocation_root_rewrite_ready_events_total,region_relocation_root_rewrite_protected_slots_total,region_relocation_root_rewrite_protected_selected_slots_total,region_relocation_root_rewrite_protected_applied_slots_total,region_relocation_root_rewrite_protected_verified_slots_total,region_relocation_root_rewrite_protected_restored_slots_total,region_relocation_root_rewrite_oblist_buckets_total,region_relocation_root_rewrite_oblist_selected_buckets_total,region_relocation_root_rewrite_oblist_applied_buckets_total,region_relocation_root_rewrite_oblist_verified_buckets_total,region_relocation_root_rewrite_oblist_restored_buckets_total,region_relocation_root_rewrite_thread_list_roots_total,region_relocation_root_rewrite_thread_list_selected_roots_total,region_relocation_root_rewrite_thread_list_applied_roots_total,region_relocation_root_rewrite_thread_list_verified_roots_total,region_relocation_root_rewrite_thread_list_restored_roots_total,region_relocation_root_rewrite_thread_object_roots_total,region_relocation_root_rewrite_thread_object_selected_roots_total,region_relocation_root_rewrite_thread_object_applied_roots_total,region_relocation_root_rewrite_thread_object_verified_roots_total,region_relocation_root_rewrite_thread_object_restored_roots_total,region_relocation_root_rewrite_thread_stack_contexts_total,region_relocation_root_rewrite_thread_stack_oldspace_contexts_total,region_relocation_root_rewrite_thread_stack_frame_bytes_total,region_relocation_root_rewrite_thread_stack_frame_root_slots_total,region_relocation_root_rewrite_thread_stack_frame_root_selected_slots_total,region_relocation_root_rewrite_thread_stack_frame_root_applied_slots_total,region_relocation_root_rewrite_thread_stack_frame_root_verified_slots_total,region_relocation_root_rewrite_thread_stack_frame_root_restored_slots_total,region_relocation_root_rewrite_thread_stack_context_root_slots_total,region_relocation_root_rewrite_thread_stack_context_root_selected_slots_total,region_relocation_root_rewrite_thread_stack_context_root_applied_slots_total,region_relocation_root_rewrite_thread_stack_context_root_verified_slots_total,region_relocation_root_rewrite_thread_stack_context_root_restored_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_selected_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_applied_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_verified_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_restored_slots_total,region_relocation_root_rewrite_prerequisite_failures_total,max_region_relocation_root_rewrite_hash,region_relocation_root_rewrite_failures_total,region_relocation_root_probe_requested_events_total,region_relocation_root_probe_ready_events_total,region_relocation_root_probe_slots_total,region_relocation_root_probe_selected_slots_total,region_relocation_root_probe_applied_slots_total,region_relocation_root_probe_verified_slots_total,region_relocation_root_probe_restored_slots_total,max_region_relocation_root_probe_hash,region_relocation_root_probe_failures_total,region_relocation_production_commit_requested_events_total,region_relocation_production_commit_ready_events_total,region_relocation_production_commit_blocked_events_total,region_relocation_production_commit_missing_prerequisites_total,region_relocation_production_commit_telemetry_release_blockers_total,region_relocation_production_commit_target_release_cleanup_blockers_total,region_relocation_production_commit_target_unlink_cleanup_blockers_total,region_relocation_production_commit_target_accounting_restore_blockers_total,max_region_relocation_production_commit_target_release_blocker_mask,region_relocation_production_commit_slot_restore_blockers_total,region_relocation_production_commit_selected_slot_restore_blockers_total,region_relocation_production_commit_external_slot_restore_blockers_total,max_region_relocation_production_commit_slot_restore_blocker_mask,region_relocation_production_commit_generated_copy_blockers_total,region_relocation_production_commit_wrapper_copy_blockers_total,region_relocation_production_commit_wrapper_forwarding_blockers_total,region_relocation_production_commit_marker_layout_blockers_total,max_region_relocation_production_commit_generated_copy_blocker_mask,region_relocation_production_commit_source_retire_blockers_total,region_relocation_production_commit_source_restore_blockers_total,region_relocation_production_commit_source_metadata_restore_blockers_total,region_relocation_production_commit_source_nonstatic_restore_blockers_total,region_relocation_production_commit_source_empty_restore_blockers_total,region_relocation_production_commit_source_chunk_restore_blockers_total,max_region_relocation_production_commit_source_restore_blocker_mask,region_relocation_production_commit_source_sweep_blockers_total,region_relocation_production_commit_source_restore_cleanup_blockers_total,region_relocation_production_commit_source_sweep_integration_blockers_total,max_region_relocation_production_commit_source_sweep_blocker_mask,region_relocation_production_commit_root_coverage_blockers_total,region_relocation_production_commit_thread_root_blockers_total,region_relocation_production_commit_thread_list_root_blockers_total,region_relocation_production_commit_thread_stack_root_blockers_total,region_relocation_production_commit_thread_context_root_blockers_total,region_relocation_production_commit_thread_frame_root_blockers_total,region_relocation_production_commit_thread_virtual_register_root_blockers_total,max_region_relocation_production_commit_thread_root_blocker_mask,max_region_relocation_production_commit_thread_stack_root_blocker_mask,region_relocation_production_commit_protected_root_blockers_total,region_relocation_production_commit_oblist_root_blockers_total,region_relocation_production_commit_count_root_blockers_total,region_relocation_production_commit_count_root_collection_blockers_total,region_relocation_production_commit_object_count_collection_blockers_total,region_relocation_production_commit_backreference_collection_blockers_total,max_region_relocation_production_commit_count_root_blocker_mask,region_relocation_production_commit_count_root_excluded_events_total,max_region_relocation_production_commit_root_coverage_blocker_mask,region_relocation_production_commit_selector_exclusion_blockers_total,region_relocation_production_commit_mark_selector_blockers_total,region_relocation_production_commit_special_selector_blockers_total,region_relocation_production_commit_space_selector_blockers_total,region_relocation_production_commit_layout_selector_blockers_total,max_region_relocation_production_commit_selector_exclusion_blocker_mask,max_region_relocation_production_commit_blocker_mask,region_relocation_production_commit_failures_total,max_mark_experiment,satb_prewrite_stores_total,satb_old_heap_refs_total,satb_old_nonstatic_refs_total,satb_old_static_refs_total,satb_old_to_younger_refs_total,satb_new_heap_refs_total,satb_queue_candidates_total,satb_queue_enqueued_total,satb_queue_overflow_total,satb_queue_drained_total,max_satb_queue_capacity,satb_thread_buffered_total,satb_thread_buffer_flushes_total,satb_thread_buffer_overflow_total,max_satb_thread_buffer_capacity,max_satb_thread_buffer_max_fill,max_satb_mark_epoch,satb_concurrent_mark_starts_total,satb_concurrent_mark_finishes_total,satb_concurrent_mark_drained_total,satb_concurrent_mark_fallbacks_total,satb_worker_start_events_total,satb_worker_start_failures_total,satb_worker_wake_events_total,satb_worker_async_drain_events_total,satb_worker_async_drained_total,satb_worker_fallback_drain_events_total,satb_worker_suspend_events_total,satb_worker_resume_events_total,satb_worker_bitmap_scan_events_total,satb_worker_bitmap_scan_segments_total,satb_worker_bitmap_scan_bits_total,satb_worker_bitmap_scan_skipped_events_total,satb_worker_mark_handoff_requested_events_total,satb_worker_mark_handoff_ready_events_total,satb_worker_mark_handoff_worker_scan_events_total,satb_worker_mark_handoff_worker_scan_bits_total,satb_worker_mark_handoff_stw_apply_objects_total,satb_worker_mark_handoff_copy_reconcile_objects_total,satb_worker_mark_handoff_generated_traversal_missing_events_total,satb_worker_mark_handoff_generated_outside_gc_missing_events_total,satb_worker_mark_handoff_generated_thread_gc_missing_events_total,satb_worker_mark_handoff_generated_special_missing_events_total,max_satb_worker_mark_handoff_generated_blocker_mask,satb_worker_mark_handoff_prerequisite_failures_total,max_satb_worker_mark_handoff_hash,satb_worker_mark_handoff_failures_total,satb_remark_drained_total,satb_shadow_marked_total,satb_shadow_duplicates_total,satb_shadow_overflow_total,satb_mark_bitmap_segments_total,satb_mark_bitmap_bytes_total,satb_mark_bitmap_apply_segments_total,satb_mark_bitmap_apply_bits_total,satb_mark_bitmap_apply_old_space_bits_total,satb_mark_bitmap_apply_mark_mode_bits_total,satb_mark_bitmap_apply_copy_mode_bits_total,satb_mark_bitmap_apply_extended_old_space_bits_total,satb_mark_bitmap_apply_extended_mark_mode_bits_total,satb_mark_bitmap_apply_extended_copy_mode_bits_total,satb_mark_bitmap_apply_extended_skipped_bits_total,satb_mark_bitmap_apply_skipped_bits_total,satb_mark_bitmap_real_apply_objects_total,satb_mark_bitmap_real_apply_extended_objects_total,satb_mark_bitmap_real_apply_interior_bits_total,satb_mark_bitmap_real_apply_skipped_bits_total,satb_mark_bitmap_real_apply_failures_total,satb_mark_bitmap_copy_reconcile_objects_total,satb_mark_bitmap_copy_reconcile_extended_objects_total,satb_mark_bitmap_copy_reconcile_forwarded_total,satb_mark_bitmap_copy_reconcile_not_forwarded_total,satb_mark_bitmap_copy_reconcile_interior_bits_total,satb_mark_bitmap_copy_reconcile_skipped_bits_total,satb_mark_bitmap_copy_reconcile_failures_total,satb_generated_prewrite_records_total,satb_dirty_card_records_total,satb_dirty_card_heap_refs_total,satb_dirty_card_younger_refs_total,max_aux_sweepers,max_requested_aux_sweepers,max_effective_aux_sweepers,max_sweeper_estimated_work_bytes,max_sweeper_min_work_bytes,max_greedy_assignment,max_remote_consecutive_batch,max_dirty_greedy_assignment,dirty_rebalanced_segments_total,dirty_rebalanced_bytes_total,max_worker_count,max_workers_with_swept_bytes,max_worker_wall_us,max_worker_accum_us,max_worker_step_us,max_worker_swept_bytes,remote_flushes_total,remote_delivery_batches_total,remote_lock_wait_total_us,remote_lock_hold_total_us,max_remote_send_batch,max_remote_delivery_batch,max_remote_receive_depth,remote_sent_total,remote_received_total,request_to_start_min_us,request_to_start_p50_us,request_to_start_p95_us,request_to_start_p99_us,request_to_start_p999_us,request_to_start_avg_us,request_to_start_max_us,collector_min_us,collector_p50_us,collector_p95_us,collector_p99_us,collector_p999_us,collector_avg_us,collector_max_us,setup_p99_us,mark_sweep_p99_us,special_p99_us,rebuild_p99_us,finish_p99_us,dirty_setup_p99_us,guardian_finalizer_p99_us,weak_pair_p99_us,ephemeron_p99_us +blocking-io,8,default,default,default,default,default,default,default,default,default,5,bsd,0.440000,0.040000,0.010000,11.36,55328,2272.73,8,0,3,2,0,2288,0,37486592,0,637,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1048576,0,0,0,64,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,9,21409,21409,21409,4287.60,21409,32,281,1116,1116,1116,422.80,1116,43,620,6,41,2,0,3,1,3 new file mode 100644 --- /dev/null +++ b/docs/gc/data/watchdog/condition-wait-summary.csv @@ -0,0 +1,2 @@ +workload,threads,collect_trip_bytes,sweeper_cap,sweeper_min_work,assignment_policy,dirty_assignment,remote_batch,descriptor_experiment,region_experiment,mark_experiment,samples,time_mode,real_seconds,user_seconds,sys_seconds,cpu_pct,max_rss_kb,iterations_per_sec,result,gc_par_samples,gc_011_samples,gc_ocd_samples,gc_oce_samples,copy_source_segments_total,mark_source_segments_total,copy_source_bytes_total,mark_source_bytes_total,max_copy_source_segments,max_mark_source_segments,max_descriptor_experiment,descriptor_source_segments_total,descriptor_source_segments_max,descriptor_max_owner_segments_max,descriptor_source_bytes_total,descriptor_source_bytes_max,descriptor_max_owner_bytes_max,descriptor_split_segments_total,descriptor_split_bytes_total,max_descriptor_split_segments,max_descriptor_split_bytes,max_region_experiment,region_barrier_stores_total,region_cross_segment_stores_total,region_cross_generation_stores_total,region_old_to_young_stores_total,region_old_to_old_cross_segment_stores_total,region_distinct_edges_total,max_region_distinct_edges,region_edge_table_overflow_total,max_region_selection_budget_bytes,max_region_candidate_segments,region_candidate_bytes_total,region_candidate_live_bytes_total,region_candidate_scan_cost_bytes_total,region_candidate_evacuation_cost_bytes_total,max_region_selected_segments,region_selected_bytes_total,region_selected_live_bytes_total,region_selected_scan_cost_bytes_total,region_selected_evacuation_cost_bytes_total,max_region_excluded_segments,max_region_excluded_mark_segments,max_region_excluded_special_segments,max_region_excluded_space_segments,max_region_excluded_layout_segments,max_region_typed_candidate_segments,max_region_typed_selected_segments,max_region_typed_layout_excluded_segments,max_region_root_candidate_segments,max_region_root_selected_segments,max_region_root_layout_excluded_segments,max_region_evacuation_experiment,max_region_evacuation_selected_segments,max_region_evacuation_eligible_segments,max_region_evacuation_blocked_segments,region_evacuation_inbound_refs_total,region_evacuation_internal_refs_total,region_evacuation_outbound_refs_total,region_evacuation_update_refs_total,region_evacuation_table_overflow_total,region_evacuation_required_bytes_total,region_evacuation_reserve_bytes_max,region_evacuation_deficit_bytes_total,region_evacuation_reserve_failures_total,region_evacuation_failure_policy_events_total,region_evacuation_failure_policy_reserve_ok_events_total,region_evacuation_failure_policy_fallback_events_total,region_evacuation_failure_policy_deficit_bytes_total,region_evacuation_failure_policy_failures_total,region_evacuation_fallback_suppressed_events_total,region_evacuation_fallback_suppressed_source_segments_total,region_evacuation_fallback_suppressed_target_segments_total,region_evacuation_fallback_suppressed_copy_bytes_total,region_evacuation_fallback_suppressed_failures_total,region_evacuation_shrink_attempt_events_total,region_evacuation_shrink_success_events_total,region_evacuation_shrink_fallback_events_total,max_region_evacuation_shrink_selected_segments,max_region_evacuation_shrink_target_segments,region_evacuation_shrink_copy_bytes_total,region_evacuation_shrink_deficit_bytes_total,region_evacuation_shrink_failures_total,max_region_relocation_plan_source_segments,max_region_relocation_plan_target_segments,region_relocation_plan_copy_bytes_total,region_relocation_plan_update_refs_total,region_relocation_plan_metadata_bytes_total,region_relocation_plan_failures_total,max_region_relocation_target_available_segments,region_relocation_target_available_bytes_max,max_region_relocation_target_sampled_segments,max_region_relocation_target_reserved_segments,region_relocation_target_reserved_bytes_total,region_relocation_target_inventory_failures_total,region_relocation_target_reservation_failures_total,region_relocation_target_unreserve_failures_total,max_region_relocation_allocator_segments,region_relocation_allocator_capacity_bytes_total,region_relocation_allocator_used_bytes_total,region_relocation_allocator_waste_bytes_total,max_region_relocation_allocator_cursor_bytes,region_relocation_allocator_failures_total,max_region_relocation_map_entries,region_relocation_map_bytes_total,max_region_relocation_map_hash,max_region_relocation_map_source_segments,max_region_relocation_map_target_segments,region_relocation_map_failures_total,max_region_relocation_object_preflight_segments,region_relocation_object_preflight_objects_total,region_relocation_object_preflight_bytes_total,region_relocation_object_preflight_excluded_segments_total,region_relocation_object_preflight_excluded_bytes_total,region_relocation_object_preflight_failures_total,region_relocation_copy_schedule_objects_total,region_relocation_copy_schedule_bytes_total,max_region_relocation_copy_schedule_target_segments,max_region_relocation_copy_schedule_cursor_bytes,region_relocation_copy_schedule_waste_bytes_total,region_relocation_copy_schedule_failures_total,region_relocation_copy_entry_objects_total,region_relocation_copy_entry_bytes_total,region_relocation_copy_entry_metadata_bytes_total,max_region_relocation_copy_entry_hash,region_relocation_copy_entry_failures_total,region_relocation_copy_bytes_objects_total,region_relocation_copy_bytes_copied_total,max_region_relocation_copy_bytes_checksum,region_relocation_copy_bytes_failures_total,region_relocation_forwarding_entries_total,region_relocation_forwarding_bytes_total,region_relocation_forwarding_metadata_bytes_total,max_region_relocation_forwarding_hash,region_relocation_forwarding_failures_total,region_relocation_forwarding_marker_objects_total,region_relocation_forwarding_marker_bytes_total,region_relocation_forwarding_marker_installed_objects_total,region_relocation_forwarding_marker_restored_objects_total,region_relocation_forwarding_marker_pair_objects_total,region_relocation_forwarding_marker_symbol_objects_total,region_relocation_forwarding_marker_vector_objects_total,region_relocation_forwarding_marker_stencil_vector_objects_total,region_relocation_forwarding_marker_box_objects_total,region_relocation_forwarding_marker_tlc_objects_total,region_relocation_forwarding_marker_ratnum_objects_total,region_relocation_forwarding_marker_exactnum_objects_total,max_region_relocation_forwarding_marker_hash,region_relocation_forwarding_marker_failures_total,region_relocation_rewrite_edges_total,region_relocation_rewrite_candidates_total,region_relocation_rewrite_forwarding_hits_total,region_relocation_rewrite_metadata_bytes_total,max_region_relocation_rewrite_hash,region_relocation_rewrite_failures_total,region_relocation_slot_rewrite_objects_total,region_relocation_slot_rewrite_slots_total,region_relocation_slot_rewrite_selected_slots_total,region_relocation_slot_rewrite_forwarding_hits_total,region_relocation_slot_rewrite_deferred_objects_total,region_relocation_slot_rewrite_deferred_bytes_total,region_relocation_slot_rewrite_deferred_typed_objects_total,region_relocation_slot_rewrite_deferred_space_objects_total,region_relocation_slot_rewrite_metadata_bytes_total,max_region_relocation_slot_rewrite_hash,region_relocation_slot_rewrite_failures_total,region_relocation_copy_slot_rewrite_slots_total,region_relocation_copy_slot_rewrite_selected_slots_total,region_relocation_copy_slot_rewrite_applied_slots_total,region_relocation_copy_slot_rewrite_deferred_slots_total,region_relocation_copy_slot_rewrite_metadata_bytes_total,max_region_relocation_copy_slot_rewrite_hash,region_relocation_copy_slot_rewrite_failures_total,region_relocation_target_publish_segments_total,region_relocation_target_publish_bytes_total,region_relocation_target_publish_objects_total,region_relocation_target_publish_forwarding_entries_total,region_relocation_target_publish_metadata_bytes_total,max_region_relocation_target_publish_hash,region_relocation_target_publish_failures_total,region_relocation_target_link_segments_total,region_relocation_target_link_bytes_total,region_relocation_target_linked_segments_total,region_relocation_target_unlinked_segments_total,region_relocation_target_link_accounting_bytes_total,max_region_relocation_target_link_hash,region_relocation_target_link_failures_total,region_relocation_external_slot_rewrite_cards_total,region_relocation_external_slot_rewrite_slots_total,region_relocation_external_slot_rewrite_selected_slots_total,region_relocation_external_slot_rewrite_forwarding_hits_total,region_relocation_external_slot_rewrite_deferred_cards_total,region_relocation_external_slot_rewrite_deferred_marked_cards_total,region_relocation_external_slot_rewrite_deferred_ambiguous_cards_total,region_relocation_external_slot_rewrite_deferred_unsupported_cards_total,region_relocation_external_slot_rewrite_deferred_special_cards_total,region_relocation_external_slot_rewrite_metadata_bytes_total,max_region_relocation_external_slot_rewrite_hash,region_relocation_external_slot_rewrite_failures_total,region_relocation_live_slot_rewrite_selected_slots_total,region_relocation_live_slot_rewrite_applied_slots_total,region_relocation_live_slot_rewrite_restored_slots_total,region_relocation_live_external_slot_rewrite_selected_slots_total,region_relocation_live_external_slot_rewrite_applied_slots_total,region_relocation_live_external_slot_rewrite_restored_slots_total,region_relocation_live_slot_rewrite_commit_ordered_slots_total,region_relocation_live_external_slot_rewrite_commit_ordered_slots_total,max_region_relocation_live_slot_rewrite_hash,region_relocation_live_slot_rewrite_failures_total,region_relocation_one_way_rewrite_selected_slots_total,region_relocation_one_way_rewrite_applied_slots_total,region_relocation_one_way_rewrite_verified_slots_total,region_relocation_one_way_rewrite_restored_slots_total,region_relocation_one_way_external_rewrite_selected_slots_total,region_relocation_one_way_external_rewrite_applied_slots_total,region_relocation_one_way_external_rewrite_verified_slots_total,region_relocation_one_way_external_rewrite_restored_slots_total,region_relocation_one_way_rewrite_prerequisite_failures_total,max_region_relocation_one_way_rewrite_hash,region_relocation_one_way_rewrite_failures_total,region_relocation_source_retire_segments_total,region_relocation_source_retire_bytes_total,region_relocation_source_retire_forwarded_objects_total,region_relocation_source_retire_target_segments_total,region_relocation_source_retire_commit_ordered_slots_total,max_region_relocation_source_retire_hash,region_relocation_source_retire_failures_total,region_relocation_source_retire_proof_segments_total,region_relocation_source_retire_proof_bytes_total,region_relocation_source_retire_proof_emptied_segments_total,region_relocation_source_retire_proof_restored_segments_total,region_relocation_source_retire_proof_nonstatic_delta_segments_total,region_relocation_source_retire_proof_empty_delta_segments_total,region_relocation_source_retire_proof_chunk_delta_segments_total,region_relocation_source_retire_proof_heap_delta_bytes_total,max_region_relocation_source_retire_proof_hash,region_relocation_source_retire_proof_failures_total,region_relocation_source_live_emit_requested_events_total,region_relocation_source_live_emit_ready_events_total,region_relocation_source_live_emit_emptied_segments_total,region_relocation_source_live_emit_bytes_total,region_relocation_source_live_emit_nonstatic_delta_segments_total,region_relocation_source_live_emit_empty_delta_segments_total,region_relocation_source_live_emit_chunk_delta_segments_total,region_relocation_source_live_emit_heap_delta_bytes_total,region_relocation_source_live_emit_cleanup_restored_segments_total,region_relocation_source_live_emit_cleanup_bytes_total,region_relocation_source_live_emit_prerequisite_failures_total,max_region_relocation_source_live_emit_hash,region_relocation_source_live_emit_failures_total,region_relocation_source_sweep_accounting_requested_events_total,region_relocation_source_sweep_accounting_ready_events_total,region_relocation_source_sweep_accounting_retired_segments_total,region_relocation_source_sweep_accounting_retired_bytes_total,region_relocation_source_sweep_accounting_nonstatic_delta_segments_total,region_relocation_source_sweep_accounting_empty_delta_segments_total,region_relocation_source_sweep_accounting_chunk_delta_segments_total,region_relocation_source_sweep_accounting_heap_delta_bytes_total,region_relocation_source_sweep_accounting_restore_cleanup_required_events_total,region_relocation_source_sweep_accounting_sweep_integration_missing_events_total,region_relocation_source_sweep_accounting_prerequisite_failures_total,max_region_relocation_source_sweep_accounting_hash,region_relocation_source_sweep_accounting_failures_total,region_relocation_target_retention_requested_events_total,region_relocation_target_retention_ready_events_total,region_relocation_target_retention_blocked_events_total,region_relocation_target_retention_missing_prerequisites_total,region_relocation_target_retention_release_blockers_total,region_relocation_target_retention_unlink_blockers_total,region_relocation_target_retention_accounting_blockers_total,max_region_relocation_target_retention_blocker_mask,region_relocation_target_retention_failures_total,region_relocation_target_retain_proof_segments_total,region_relocation_target_retain_proof_bytes_total,region_relocation_target_retain_proof_linked_segments_total,region_relocation_target_retain_proof_unlinked_segments_total,region_relocation_target_retain_proof_accounting_bytes_total,region_relocation_target_retain_proof_heap_delta_bytes_total,max_region_relocation_target_retain_proof_hash,region_relocation_target_retain_proof_failures_total,region_relocation_target_release_suppression_requested_events_total,region_relocation_target_release_suppression_ready_events_total,region_relocation_target_release_suppression_suppressed_segments_total,region_relocation_target_release_suppression_suppressed_bytes_total,region_relocation_target_release_suppression_retained_segments_total,region_relocation_target_release_suppression_retained_bytes_total,region_relocation_target_release_suppression_cleanup_segments_total,region_relocation_target_release_suppression_cleanup_bytes_total,region_relocation_target_release_suppression_prerequisite_failures_total,max_region_relocation_target_release_suppression_hash,region_relocation_target_release_suppression_failures_total,region_relocation_target_live_emit_requested_events_total,region_relocation_target_live_emit_ready_events_total,region_relocation_target_live_emit_linked_segments_total,region_relocation_target_live_emit_linked_bytes_total,region_relocation_target_live_emit_heap_delta_bytes_total,region_relocation_target_live_emit_cleanup_unlinked_segments_total,region_relocation_target_live_emit_cleanup_released_segments_total,region_relocation_target_live_emit_cleanup_bytes_total,region_relocation_target_live_emit_prerequisite_failures_total,max_region_relocation_target_live_emit_hash,region_relocation_target_live_emit_failures_total,region_relocation_slot_live_emit_selected_slots_total,region_relocation_slot_live_emit_applied_slots_total,region_relocation_slot_live_emit_verified_slots_total,region_relocation_slot_live_emit_restored_slots_total,region_relocation_slot_live_emit_external_selected_slots_total,region_relocation_slot_live_emit_external_applied_slots_total,region_relocation_slot_live_emit_external_verified_slots_total,region_relocation_slot_live_emit_external_restored_slots_total,region_relocation_slot_live_emit_prerequisite_failures_total,max_region_relocation_slot_live_emit_hash,region_relocation_slot_live_emit_failures_total,region_relocation_root_rewrite_requested_events_total,region_relocation_root_rewrite_ready_events_total,region_relocation_root_rewrite_protected_slots_total,region_relocation_root_rewrite_protected_selected_slots_total,region_relocation_root_rewrite_protected_applied_slots_total,region_relocation_root_rewrite_protected_verified_slots_total,region_relocation_root_rewrite_protected_restored_slots_total,region_relocation_root_rewrite_oblist_buckets_total,region_relocation_root_rewrite_oblist_selected_buckets_total,region_relocation_root_rewrite_oblist_applied_buckets_total,region_relocation_root_rewrite_oblist_verified_buckets_total,region_relocation_root_rewrite_oblist_restored_buckets_total,region_relocation_root_rewrite_thread_list_roots_total,region_relocation_root_rewrite_thread_list_selected_roots_total,region_relocation_root_rewrite_thread_list_applied_roots_total,region_relocation_root_rewrite_thread_list_verified_roots_total,region_relocation_root_rewrite_thread_list_restored_roots_total,region_relocation_root_rewrite_thread_object_roots_total,region_relocation_root_rewrite_thread_object_selected_roots_total,region_relocation_root_rewrite_thread_object_applied_roots_total,region_relocation_root_rewrite_thread_object_verified_roots_total,region_relocation_root_rewrite_thread_object_restored_roots_total,region_relocation_root_rewrite_thread_stack_contexts_total,region_relocation_root_rewrite_thread_stack_oldspace_contexts_total,region_relocation_root_rewrite_thread_stack_frame_bytes_total,region_relocation_root_rewrite_thread_stack_frame_root_slots_total,region_relocation_root_rewrite_thread_stack_frame_root_selected_slots_total,region_relocation_root_rewrite_thread_stack_frame_root_applied_slots_total,region_relocation_root_rewrite_thread_stack_frame_root_verified_slots_total,region_relocation_root_rewrite_thread_stack_frame_root_restored_slots_total,region_relocation_root_rewrite_thread_stack_context_root_slots_total,region_relocation_root_rewrite_thread_stack_context_root_selected_slots_total,region_relocation_root_rewrite_thread_stack_context_root_applied_slots_total,region_relocation_root_rewrite_thread_stack_context_root_verified_slots_total,region_relocation_root_rewrite_thread_stack_context_root_restored_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_selected_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_applied_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_verified_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_restored_slots_total,region_relocation_root_rewrite_prerequisite_failures_total,max_region_relocation_root_rewrite_hash,region_relocation_root_rewrite_failures_total,region_relocation_root_probe_requested_events_total,region_relocation_root_probe_ready_events_total,region_relocation_root_probe_slots_total,region_relocation_root_probe_selected_slots_total,region_relocation_root_probe_applied_slots_total,region_relocation_root_probe_verified_slots_total,region_relocation_root_probe_restored_slots_total,max_region_relocation_root_probe_hash,region_relocation_root_probe_failures_total,region_relocation_production_commit_requested_events_total,region_relocation_production_commit_ready_events_total,region_relocation_production_commit_blocked_events_total,region_relocation_production_commit_missing_prerequisites_total,region_relocation_production_commit_telemetry_release_blockers_total,region_relocation_production_commit_target_release_cleanup_blockers_total,region_relocation_production_commit_target_unlink_cleanup_blockers_total,region_relocation_production_commit_target_accounting_restore_blockers_total,max_region_relocation_production_commit_target_release_blocker_mask,region_relocation_production_commit_slot_restore_blockers_total,region_relocation_production_commit_selected_slot_restore_blockers_total,region_relocation_production_commit_external_slot_restore_blockers_total,max_region_relocation_production_commit_slot_restore_blocker_mask,region_relocation_production_commit_generated_copy_blockers_total,region_relocation_production_commit_wrapper_copy_blockers_total,region_relocation_production_commit_wrapper_forwarding_blockers_total,region_relocation_production_commit_marker_layout_blockers_total,max_region_relocation_production_commit_generated_copy_blocker_mask,region_relocation_production_commit_source_retire_blockers_total,region_relocation_production_commit_source_restore_blockers_total,region_relocation_production_commit_source_metadata_restore_blockers_total,region_relocation_production_commit_source_nonstatic_restore_blockers_total,region_relocation_production_commit_source_empty_restore_blockers_total,region_relocation_production_commit_source_chunk_restore_blockers_total,max_region_relocation_production_commit_source_restore_blocker_mask,region_relocation_production_commit_source_sweep_blockers_total,region_relocation_production_commit_source_restore_cleanup_blockers_total,region_relocation_production_commit_source_sweep_integration_blockers_total,max_region_relocation_production_commit_source_sweep_blocker_mask,region_relocation_production_commit_root_coverage_blockers_total,region_relocation_production_commit_thread_root_blockers_total,region_relocation_production_commit_thread_list_root_blockers_total,region_relocation_production_commit_thread_stack_root_blockers_total,region_relocation_production_commit_thread_context_root_blockers_total,region_relocation_production_commit_thread_frame_root_blockers_total,region_relocation_production_commit_thread_virtual_register_root_blockers_total,max_region_relocation_production_commit_thread_root_blocker_mask,max_region_relocation_production_commit_thread_stack_root_blocker_mask,region_relocation_production_commit_protected_root_blockers_total,region_relocation_production_commit_oblist_root_blockers_total,region_relocation_production_commit_count_root_blockers_total,region_relocation_production_commit_count_root_collection_blockers_total,region_relocation_production_commit_object_count_collection_blockers_total,region_relocation_production_commit_backreference_collection_blockers_total,max_region_relocation_production_commit_count_root_blocker_mask,region_relocation_production_commit_count_root_excluded_events_total,max_region_relocation_production_commit_root_coverage_blocker_mask,region_relocation_production_commit_selector_exclusion_blockers_total,region_relocation_production_commit_mark_selector_blockers_total,region_relocation_production_commit_special_selector_blockers_total,region_relocation_production_commit_space_selector_blockers_total,region_relocation_production_commit_layout_selector_blockers_total,max_region_relocation_production_commit_selector_exclusion_blocker_mask,max_region_relocation_production_commit_blocker_mask,region_relocation_production_commit_failures_total,max_mark_experiment,satb_prewrite_stores_total,satb_old_heap_refs_total,satb_old_nonstatic_refs_total,satb_old_static_refs_total,satb_old_to_younger_refs_total,satb_new_heap_refs_total,satb_queue_candidates_total,satb_queue_enqueued_total,satb_queue_overflow_total,satb_queue_drained_total,max_satb_queue_capacity,satb_thread_buffered_total,satb_thread_buffer_flushes_total,satb_thread_buffer_overflow_total,max_satb_thread_buffer_capacity,max_satb_thread_buffer_max_fill,max_satb_mark_epoch,satb_concurrent_mark_starts_total,satb_concurrent_mark_finishes_total,satb_concurrent_mark_drained_total,satb_concurrent_mark_fallbacks_total,satb_worker_start_events_total,satb_worker_start_failures_total,satb_worker_wake_events_total,satb_worker_async_drain_events_total,satb_worker_async_drained_total,satb_worker_fallback_drain_events_total,satb_worker_suspend_events_total,satb_worker_resume_events_total,satb_worker_bitmap_scan_events_total,satb_worker_bitmap_scan_segments_total,satb_worker_bitmap_scan_bits_total,satb_worker_bitmap_scan_skipped_events_total,satb_worker_mark_handoff_requested_events_total,satb_worker_mark_handoff_ready_events_total,satb_worker_mark_handoff_worker_scan_events_total,satb_worker_mark_handoff_worker_scan_bits_total,satb_worker_mark_handoff_stw_apply_objects_total,satb_worker_mark_handoff_copy_reconcile_objects_total,satb_worker_mark_handoff_generated_traversal_missing_events_total,satb_worker_mark_handoff_generated_outside_gc_missing_events_total,satb_worker_mark_handoff_generated_thread_gc_missing_events_total,satb_worker_mark_handoff_generated_special_missing_events_total,max_satb_worker_mark_handoff_generated_blocker_mask,satb_worker_mark_handoff_prerequisite_failures_total,max_satb_worker_mark_handoff_hash,satb_worker_mark_handoff_failures_total,satb_remark_drained_total,satb_shadow_marked_total,satb_shadow_duplicates_total,satb_shadow_overflow_total,satb_mark_bitmap_segments_total,satb_mark_bitmap_bytes_total,satb_mark_bitmap_apply_segments_total,satb_mark_bitmap_apply_bits_total,satb_mark_bitmap_apply_old_space_bits_total,satb_mark_bitmap_apply_mark_mode_bits_total,satb_mark_bitmap_apply_copy_mode_bits_total,satb_mark_bitmap_apply_extended_old_space_bits_total,satb_mark_bitmap_apply_extended_mark_mode_bits_total,satb_mark_bitmap_apply_extended_copy_mode_bits_total,satb_mark_bitmap_apply_extended_skipped_bits_total,satb_mark_bitmap_apply_skipped_bits_total,satb_mark_bitmap_real_apply_objects_total,satb_mark_bitmap_real_apply_extended_objects_total,satb_mark_bitmap_real_apply_interior_bits_total,satb_mark_bitmap_real_apply_skipped_bits_total,satb_mark_bitmap_real_apply_failures_total,satb_mark_bitmap_copy_reconcile_objects_total,satb_mark_bitmap_copy_reconcile_extended_objects_total,satb_mark_bitmap_copy_reconcile_forwarded_total,satb_mark_bitmap_copy_reconcile_not_forwarded_total,satb_mark_bitmap_copy_reconcile_interior_bits_total,satb_mark_bitmap_copy_reconcile_skipped_bits_total,satb_mark_bitmap_copy_reconcile_failures_total,satb_generated_prewrite_records_total,satb_dirty_card_records_total,satb_dirty_card_heap_refs_total,satb_dirty_card_younger_refs_total,max_aux_sweepers,max_requested_aux_sweepers,max_effective_aux_sweepers,max_sweeper_estimated_work_bytes,max_sweeper_min_work_bytes,max_greedy_assignment,max_remote_consecutive_batch,max_dirty_greedy_assignment,dirty_rebalanced_segments_total,dirty_rebalanced_bytes_total,max_worker_count,max_workers_with_swept_bytes,max_worker_wall_us,max_worker_accum_us,max_worker_step_us,max_worker_swept_bytes,remote_flushes_total,remote_delivery_batches_total,remote_lock_wait_total_us,remote_lock_hold_total_us,max_remote_send_batch,max_remote_delivery_batch,max_remote_receive_depth,remote_sent_total,remote_received_total,request_to_start_min_us,request_to_start_p50_us,request_to_start_p95_us,request_to_start_p99_us,request_to_start_p999_us,request_to_start_avg_us,request_to_start_max_us,collector_min_us,collector_p50_us,collector_p95_us,collector_p99_us,collector_p999_us,collector_avg_us,collector_max_us,setup_p99_us,mark_sweep_p99_us,special_p99_us,rebuild_p99_us,finish_p99_us,dirty_setup_p99_us,guardian_finalizer_p99_us,weak_pair_p99_us,ephemeron_p99_us +condition-wait,8,default,default,default,default,default,default,default,default,default,5,bsd,0.360000,0.040000,0.010000,13.89,55328,2777.78,8,0,3,2,0,2288,0,37486592,0,637,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1048576,0,0,0,64,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,27722,27722,27722,5551.00,27722,20,154,701,701,701,317.20,701,17,655,8,44,2,0,4,1,4 new file mode 100644 --- /dev/null +++ b/docs/gc/data/watchdog/safe-user-mutex-summary.csv @@ -0,0 +1,2 @@ +workload,threads,collect_trip_bytes,sweeper_cap,sweeper_min_work,assignment_policy,dirty_assignment,remote_batch,descriptor_experiment,region_experiment,mark_experiment,samples,time_mode,real_seconds,user_seconds,sys_seconds,cpu_pct,max_rss_kb,iterations_per_sec,result,gc_par_samples,gc_011_samples,gc_ocd_samples,gc_oce_samples,copy_source_segments_total,mark_source_segments_total,copy_source_bytes_total,mark_source_bytes_total,max_copy_source_segments,max_mark_source_segments,max_descriptor_experiment,descriptor_source_segments_total,descriptor_source_segments_max,descriptor_max_owner_segments_max,descriptor_source_bytes_total,descriptor_source_bytes_max,descriptor_max_owner_bytes_max,descriptor_split_segments_total,descriptor_split_bytes_total,max_descriptor_split_segments,max_descriptor_split_bytes,max_region_experiment,region_barrier_stores_total,region_cross_segment_stores_total,region_cross_generation_stores_total,region_old_to_young_stores_total,region_old_to_old_cross_segment_stores_total,region_distinct_edges_total,max_region_distinct_edges,region_edge_table_overflow_total,max_region_selection_budget_bytes,max_region_candidate_segments,region_candidate_bytes_total,region_candidate_live_bytes_total,region_candidate_scan_cost_bytes_total,region_candidate_evacuation_cost_bytes_total,max_region_selected_segments,region_selected_bytes_total,region_selected_live_bytes_total,region_selected_scan_cost_bytes_total,region_selected_evacuation_cost_bytes_total,max_region_excluded_segments,max_region_excluded_mark_segments,max_region_excluded_special_segments,max_region_excluded_space_segments,max_region_excluded_layout_segments,max_region_typed_candidate_segments,max_region_typed_selected_segments,max_region_typed_layout_excluded_segments,max_region_root_candidate_segments,max_region_root_selected_segments,max_region_root_layout_excluded_segments,max_region_evacuation_experiment,max_region_evacuation_selected_segments,max_region_evacuation_eligible_segments,max_region_evacuation_blocked_segments,region_evacuation_inbound_refs_total,region_evacuation_internal_refs_total,region_evacuation_outbound_refs_total,region_evacuation_update_refs_total,region_evacuation_table_overflow_total,region_evacuation_required_bytes_total,region_evacuation_reserve_bytes_max,region_evacuation_deficit_bytes_total,region_evacuation_reserve_failures_total,region_evacuation_failure_policy_events_total,region_evacuation_failure_policy_reserve_ok_events_total,region_evacuation_failure_policy_fallback_events_total,region_evacuation_failure_policy_deficit_bytes_total,region_evacuation_failure_policy_failures_total,region_evacuation_fallback_suppressed_events_total,region_evacuation_fallback_suppressed_source_segments_total,region_evacuation_fallback_suppressed_target_segments_total,region_evacuation_fallback_suppressed_copy_bytes_total,region_evacuation_fallback_suppressed_failures_total,region_evacuation_shrink_attempt_events_total,region_evacuation_shrink_success_events_total,region_evacuation_shrink_fallback_events_total,max_region_evacuation_shrink_selected_segments,max_region_evacuation_shrink_target_segments,region_evacuation_shrink_copy_bytes_total,region_evacuation_shrink_deficit_bytes_total,region_evacuation_shrink_failures_total,max_region_relocation_plan_source_segments,max_region_relocation_plan_target_segments,region_relocation_plan_copy_bytes_total,region_relocation_plan_update_refs_total,region_relocation_plan_metadata_bytes_total,region_relocation_plan_failures_total,max_region_relocation_target_available_segments,region_relocation_target_available_bytes_max,max_region_relocation_target_sampled_segments,max_region_relocation_target_reserved_segments,region_relocation_target_reserved_bytes_total,region_relocation_target_inventory_failures_total,region_relocation_target_reservation_failures_total,region_relocation_target_unreserve_failures_total,max_region_relocation_allocator_segments,region_relocation_allocator_capacity_bytes_total,region_relocation_allocator_used_bytes_total,region_relocation_allocator_waste_bytes_total,max_region_relocation_allocator_cursor_bytes,region_relocation_allocator_failures_total,max_region_relocation_map_entries,region_relocation_map_bytes_total,max_region_relocation_map_hash,max_region_relocation_map_source_segments,max_region_relocation_map_target_segments,region_relocation_map_failures_total,max_region_relocation_object_preflight_segments,region_relocation_object_preflight_objects_total,region_relocation_object_preflight_bytes_total,region_relocation_object_preflight_excluded_segments_total,region_relocation_object_preflight_excluded_bytes_total,region_relocation_object_preflight_failures_total,region_relocation_copy_schedule_objects_total,region_relocation_copy_schedule_bytes_total,max_region_relocation_copy_schedule_target_segments,max_region_relocation_copy_schedule_cursor_bytes,region_relocation_copy_schedule_waste_bytes_total,region_relocation_copy_schedule_failures_total,region_relocation_copy_entry_objects_total,region_relocation_copy_entry_bytes_total,region_relocation_copy_entry_metadata_bytes_total,max_region_relocation_copy_entry_hash,region_relocation_copy_entry_failures_total,region_relocation_copy_bytes_objects_total,region_relocation_copy_bytes_copied_total,max_region_relocation_copy_bytes_checksum,region_relocation_copy_bytes_failures_total,region_relocation_forwarding_entries_total,region_relocation_forwarding_bytes_total,region_relocation_forwarding_metadata_bytes_total,max_region_relocation_forwarding_hash,region_relocation_forwarding_failures_total,region_relocation_forwarding_marker_objects_total,region_relocation_forwarding_marker_bytes_total,region_relocation_forwarding_marker_installed_objects_total,region_relocation_forwarding_marker_restored_objects_total,region_relocation_forwarding_marker_pair_objects_total,region_relocation_forwarding_marker_symbol_objects_total,region_relocation_forwarding_marker_vector_objects_total,region_relocation_forwarding_marker_stencil_vector_objects_total,region_relocation_forwarding_marker_box_objects_total,region_relocation_forwarding_marker_tlc_objects_total,region_relocation_forwarding_marker_ratnum_objects_total,region_relocation_forwarding_marker_exactnum_objects_total,max_region_relocation_forwarding_marker_hash,region_relocation_forwarding_marker_failures_total,region_relocation_rewrite_edges_total,region_relocation_rewrite_candidates_total,region_relocation_rewrite_forwarding_hits_total,region_relocation_rewrite_metadata_bytes_total,max_region_relocation_rewrite_hash,region_relocation_rewrite_failures_total,region_relocation_slot_rewrite_objects_total,region_relocation_slot_rewrite_slots_total,region_relocation_slot_rewrite_selected_slots_total,region_relocation_slot_rewrite_forwarding_hits_total,region_relocation_slot_rewrite_deferred_objects_total,region_relocation_slot_rewrite_deferred_bytes_total,region_relocation_slot_rewrite_deferred_typed_objects_total,region_relocation_slot_rewrite_deferred_space_objects_total,region_relocation_slot_rewrite_metadata_bytes_total,max_region_relocation_slot_rewrite_hash,region_relocation_slot_rewrite_failures_total,region_relocation_copy_slot_rewrite_slots_total,region_relocation_copy_slot_rewrite_selected_slots_total,region_relocation_copy_slot_rewrite_applied_slots_total,region_relocation_copy_slot_rewrite_deferred_slots_total,region_relocation_copy_slot_rewrite_metadata_bytes_total,max_region_relocation_copy_slot_rewrite_hash,region_relocation_copy_slot_rewrite_failures_total,region_relocation_target_publish_segments_total,region_relocation_target_publish_bytes_total,region_relocation_target_publish_objects_total,region_relocation_target_publish_forwarding_entries_total,region_relocation_target_publish_metadata_bytes_total,max_region_relocation_target_publish_hash,region_relocation_target_publish_failures_total,region_relocation_target_link_segments_total,region_relocation_target_link_bytes_total,region_relocation_target_linked_segments_total,region_relocation_target_unlinked_segments_total,region_relocation_target_link_accounting_bytes_total,max_region_relocation_target_link_hash,region_relocation_target_link_failures_total,region_relocation_external_slot_rewrite_cards_total,region_relocation_external_slot_rewrite_slots_total,region_relocation_external_slot_rewrite_selected_slots_total,region_relocation_external_slot_rewrite_forwarding_hits_total,region_relocation_external_slot_rewrite_deferred_cards_total,region_relocation_external_slot_rewrite_deferred_marked_cards_total,region_relocation_external_slot_rewrite_deferred_ambiguous_cards_total,region_relocation_external_slot_rewrite_deferred_unsupported_cards_total,region_relocation_external_slot_rewrite_deferred_special_cards_total,region_relocation_external_slot_rewrite_metadata_bytes_total,max_region_relocation_external_slot_rewrite_hash,region_relocation_external_slot_rewrite_failures_total,region_relocation_live_slot_rewrite_selected_slots_total,region_relocation_live_slot_rewrite_applied_slots_total,region_relocation_live_slot_rewrite_restored_slots_total,region_relocation_live_external_slot_rewrite_selected_slots_total,region_relocation_live_external_slot_rewrite_applied_slots_total,region_relocation_live_external_slot_rewrite_restored_slots_total,region_relocation_live_slot_rewrite_commit_ordered_slots_total,region_relocation_live_external_slot_rewrite_commit_ordered_slots_total,max_region_relocation_live_slot_rewrite_hash,region_relocation_live_slot_rewrite_failures_total,region_relocation_one_way_rewrite_selected_slots_total,region_relocation_one_way_rewrite_applied_slots_total,region_relocation_one_way_rewrite_verified_slots_total,region_relocation_one_way_rewrite_restored_slots_total,region_relocation_one_way_external_rewrite_selected_slots_total,region_relocation_one_way_external_rewrite_applied_slots_total,region_relocation_one_way_external_rewrite_verified_slots_total,region_relocation_one_way_external_rewrite_restored_slots_total,region_relocation_one_way_rewrite_prerequisite_failures_total,max_region_relocation_one_way_rewrite_hash,region_relocation_one_way_rewrite_failures_total,region_relocation_source_retire_segments_total,region_relocation_source_retire_bytes_total,region_relocation_source_retire_forwarded_objects_total,region_relocation_source_retire_target_segments_total,region_relocation_source_retire_commit_ordered_slots_total,max_region_relocation_source_retire_hash,region_relocation_source_retire_failures_total,region_relocation_source_retire_proof_segments_total,region_relocation_source_retire_proof_bytes_total,region_relocation_source_retire_proof_emptied_segments_total,region_relocation_source_retire_proof_restored_segments_total,region_relocation_source_retire_proof_nonstatic_delta_segments_total,region_relocation_source_retire_proof_empty_delta_segments_total,region_relocation_source_retire_proof_chunk_delta_segments_total,region_relocation_source_retire_proof_heap_delta_bytes_total,max_region_relocation_source_retire_proof_hash,region_relocation_source_retire_proof_failures_total,region_relocation_source_live_emit_requested_events_total,region_relocation_source_live_emit_ready_events_total,region_relocation_source_live_emit_emptied_segments_total,region_relocation_source_live_emit_bytes_total,region_relocation_source_live_emit_nonstatic_delta_segments_total,region_relocation_source_live_emit_empty_delta_segments_total,region_relocation_source_live_emit_chunk_delta_segments_total,region_relocation_source_live_emit_heap_delta_bytes_total,region_relocation_source_live_emit_cleanup_restored_segments_total,region_relocation_source_live_emit_cleanup_bytes_total,region_relocation_source_live_emit_prerequisite_failures_total,max_region_relocation_source_live_emit_hash,region_relocation_source_live_emit_failures_total,region_relocation_source_sweep_accounting_requested_events_total,region_relocation_source_sweep_accounting_ready_events_total,region_relocation_source_sweep_accounting_retired_segments_total,region_relocation_source_sweep_accounting_retired_bytes_total,region_relocation_source_sweep_accounting_nonstatic_delta_segments_total,region_relocation_source_sweep_accounting_empty_delta_segments_total,region_relocation_source_sweep_accounting_chunk_delta_segments_total,region_relocation_source_sweep_accounting_heap_delta_bytes_total,region_relocation_source_sweep_accounting_restore_cleanup_required_events_total,region_relocation_source_sweep_accounting_sweep_integration_missing_events_total,region_relocation_source_sweep_accounting_prerequisite_failures_total,max_region_relocation_source_sweep_accounting_hash,region_relocation_source_sweep_accounting_failures_total,region_relocation_target_retention_requested_events_total,region_relocation_target_retention_ready_events_total,region_relocation_target_retention_blocked_events_total,region_relocation_target_retention_missing_prerequisites_total,region_relocation_target_retention_release_blockers_total,region_relocation_target_retention_unlink_blockers_total,region_relocation_target_retention_accounting_blockers_total,max_region_relocation_target_retention_blocker_mask,region_relocation_target_retention_failures_total,region_relocation_target_retain_proof_segments_total,region_relocation_target_retain_proof_bytes_total,region_relocation_target_retain_proof_linked_segments_total,region_relocation_target_retain_proof_unlinked_segments_total,region_relocation_target_retain_proof_accounting_bytes_total,region_relocation_target_retain_proof_heap_delta_bytes_total,max_region_relocation_target_retain_proof_hash,region_relocation_target_retain_proof_failures_total,region_relocation_target_release_suppression_requested_events_total,region_relocation_target_release_suppression_ready_events_total,region_relocation_target_release_suppression_suppressed_segments_total,region_relocation_target_release_suppression_suppressed_bytes_total,region_relocation_target_release_suppression_retained_segments_total,region_relocation_target_release_suppression_retained_bytes_total,region_relocation_target_release_suppression_cleanup_segments_total,region_relocation_target_release_suppression_cleanup_bytes_total,region_relocation_target_release_suppression_prerequisite_failures_total,max_region_relocation_target_release_suppression_hash,region_relocation_target_release_suppression_failures_total,region_relocation_target_live_emit_requested_events_total,region_relocation_target_live_emit_ready_events_total,region_relocation_target_live_emit_linked_segments_total,region_relocation_target_live_emit_linked_bytes_total,region_relocation_target_live_emit_heap_delta_bytes_total,region_relocation_target_live_emit_cleanup_unlinked_segments_total,region_relocation_target_live_emit_cleanup_released_segments_total,region_relocation_target_live_emit_cleanup_bytes_total,region_relocation_target_live_emit_prerequisite_failures_total,max_region_relocation_target_live_emit_hash,region_relocation_target_live_emit_failures_total,region_relocation_slot_live_emit_selected_slots_total,region_relocation_slot_live_emit_applied_slots_total,region_relocation_slot_live_emit_verified_slots_total,region_relocation_slot_live_emit_restored_slots_total,region_relocation_slot_live_emit_external_selected_slots_total,region_relocation_slot_live_emit_external_applied_slots_total,region_relocation_slot_live_emit_external_verified_slots_total,region_relocation_slot_live_emit_external_restored_slots_total,region_relocation_slot_live_emit_prerequisite_failures_total,max_region_relocation_slot_live_emit_hash,region_relocation_slot_live_emit_failures_total,region_relocation_root_rewrite_requested_events_total,region_relocation_root_rewrite_ready_events_total,region_relocation_root_rewrite_protected_slots_total,region_relocation_root_rewrite_protected_selected_slots_total,region_relocation_root_rewrite_protected_applied_slots_total,region_relocation_root_rewrite_protected_verified_slots_total,region_relocation_root_rewrite_protected_restored_slots_total,region_relocation_root_rewrite_oblist_buckets_total,region_relocation_root_rewrite_oblist_selected_buckets_total,region_relocation_root_rewrite_oblist_applied_buckets_total,region_relocation_root_rewrite_oblist_verified_buckets_total,region_relocation_root_rewrite_oblist_restored_buckets_total,region_relocation_root_rewrite_thread_list_roots_total,region_relocation_root_rewrite_thread_list_selected_roots_total,region_relocation_root_rewrite_thread_list_applied_roots_total,region_relocation_root_rewrite_thread_list_verified_roots_total,region_relocation_root_rewrite_thread_list_restored_roots_total,region_relocation_root_rewrite_thread_object_roots_total,region_relocation_root_rewrite_thread_object_selected_roots_total,region_relocation_root_rewrite_thread_object_applied_roots_total,region_relocation_root_rewrite_thread_object_verified_roots_total,region_relocation_root_rewrite_thread_object_restored_roots_total,region_relocation_root_rewrite_thread_stack_contexts_total,region_relocation_root_rewrite_thread_stack_oldspace_contexts_total,region_relocation_root_rewrite_thread_stack_frame_bytes_total,region_relocation_root_rewrite_thread_stack_frame_root_slots_total,region_relocation_root_rewrite_thread_stack_frame_root_selected_slots_total,region_relocation_root_rewrite_thread_stack_frame_root_applied_slots_total,region_relocation_root_rewrite_thread_stack_frame_root_verified_slots_total,region_relocation_root_rewrite_thread_stack_frame_root_restored_slots_total,region_relocation_root_rewrite_thread_stack_context_root_slots_total,region_relocation_root_rewrite_thread_stack_context_root_selected_slots_total,region_relocation_root_rewrite_thread_stack_context_root_applied_slots_total,region_relocation_root_rewrite_thread_stack_context_root_verified_slots_total,region_relocation_root_rewrite_thread_stack_context_root_restored_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_selected_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_applied_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_verified_slots_total,region_relocation_root_rewrite_thread_stack_virtual_register_restored_slots_total,region_relocation_root_rewrite_prerequisite_failures_total,max_region_relocation_root_rewrite_hash,region_relocation_root_rewrite_failures_total,region_relocation_root_probe_requested_events_total,region_relocation_root_probe_ready_events_total,region_relocation_root_probe_slots_total,region_relocation_root_probe_selected_slots_total,region_relocation_root_probe_applied_slots_total,region_relocation_root_probe_verified_slots_total,region_relocation_root_probe_restored_slots_total,max_region_relocation_root_probe_hash,region_relocation_root_probe_failures_total,region_relocation_production_commit_requested_events_total,region_relocation_production_commit_ready_events_total,region_relocation_production_commit_blocked_events_total,region_relocation_production_commit_missing_prerequisites_total,region_relocation_production_commit_telemetry_release_blockers_total,region_relocation_production_commit_target_release_cleanup_blockers_total,region_relocation_production_commit_target_unlink_cleanup_blockers_total,region_relocation_production_commit_target_accounting_restore_blockers_total,max_region_relocation_production_commit_target_release_blocker_mask,region_relocation_production_commit_slot_restore_blockers_total,region_relocation_production_commit_selected_slot_restore_blockers_total,region_relocation_production_commit_external_slot_restore_blockers_total,max_region_relocation_production_commit_slot_restore_blocker_mask,region_relocation_production_commit_generated_copy_blockers_total,region_relocation_production_commit_wrapper_copy_blockers_total,region_relocation_production_commit_wrapper_forwarding_blockers_total,region_relocation_production_commit_marker_layout_blockers_total,max_region_relocation_production_commit_generated_copy_blocker_mask,region_relocation_production_commit_source_retire_blockers_total,region_relocation_production_commit_source_restore_blockers_total,region_relocation_production_commit_source_metadata_restore_blockers_total,region_relocation_production_commit_source_nonstatic_restore_blockers_total,region_relocation_production_commit_source_empty_restore_blockers_total,region_relocation_production_commit_source_chunk_restore_blockers_total,max_region_relocation_production_commit_source_restore_blocker_mask,region_relocation_production_commit_source_sweep_blockers_total,region_relocation_production_commit_source_restore_cleanup_blockers_total,region_relocation_production_commit_source_sweep_integration_blockers_total,max_region_relocation_production_commit_source_sweep_blocker_mask,region_relocation_production_commit_root_coverage_blockers_total,region_relocation_production_commit_thread_root_blockers_total,region_relocation_production_commit_thread_list_root_blockers_total,region_relocation_production_commit_thread_stack_root_blockers_total,region_relocation_production_commit_thread_context_root_blockers_total,region_relocation_production_commit_thread_frame_root_blockers_total,region_relocation_production_commit_thread_virtual_register_root_blockers_total,max_region_relocation_production_commit_thread_root_blocker_mask,max_region_relocation_production_commit_thread_stack_root_blocker_mask,region_relocation_production_commit_protected_root_blockers_total,region_relocation_production_commit_oblist_root_blockers_total,region_relocation_production_commit_count_root_blockers_total,region_relocation_production_commit_count_root_collection_blockers_total,region_relocation_production_commit_object_count_collection_blockers_total,region_relocation_production_commit_backreference_collection_blockers_total,max_region_relocation_production_commit_count_root_blocker_mask,region_relocation_production_commit_count_root_excluded_events_total,max_region_relocation_production_commit_root_coverage_blocker_mask,region_relocation_production_commit_selector_exclusion_blockers_total,region_relocation_production_commit_mark_selector_blockers_total,region_relocation_production_commit_special_selector_blockers_total,region_relocation_production_commit_space_selector_blockers_total,region_relocation_production_commit_layout_selector_blockers_total,max_region_relocation_production_commit_selector_exclusion_blocker_mask,max_region_relocation_production_commit_blocker_mask,region_relocation_production_commit_failures_total,max_mark_experiment,satb_prewrite_stores_total,satb_old_heap_refs_total,satb_old_nonstatic_refs_total,satb_old_static_refs_total,satb_old_to_younger_refs_total,satb_new_heap_refs_total,satb_queue_candidates_total,satb_queue_enqueued_total,satb_queue_overflow_total,satb_queue_drained_total,max_satb_queue_capacity,satb_thread_buffered_total,satb_thread_buffer_flushes_total,satb_thread_buffer_overflow_total,max_satb_thread_buffer_capacity,max_satb_thread_buffer_max_fill,max_satb_mark_epoch,satb_concurrent_mark_starts_total,satb_concurrent_mark_finishes_total,satb_concurrent_mark_drained_total,satb_concurrent_mark_fallbacks_total,satb_worker_start_events_total,satb_worker_start_failures_total,satb_worker_wake_events_total,satb_worker_async_drain_events_total,satb_worker_async_drained_total,satb_worker_fallback_drain_events_total,satb_worker_suspend_events_total,satb_worker_resume_events_total,satb_worker_bitmap_scan_events_total,satb_worker_bitmap_scan_segments_total,satb_worker_bitmap_scan_bits_total,satb_worker_bitmap_scan_skipped_events_total,satb_worker_mark_handoff_requested_events_total,satb_worker_mark_handoff_ready_events_total,satb_worker_mark_handoff_worker_scan_events_total,satb_worker_mark_handoff_worker_scan_bits_total,satb_worker_mark_handoff_stw_apply_objects_total,satb_worker_mark_handoff_copy_reconcile_objects_total,satb_worker_mark_handoff_generated_traversal_missing_events_total,satb_worker_mark_handoff_generated_outside_gc_missing_events_total,satb_worker_mark_handoff_generated_thread_gc_missing_events_total,satb_worker_mark_handoff_generated_special_missing_events_total,max_satb_worker_mark_handoff_generated_blocker_mask,satb_worker_mark_handoff_prerequisite_failures_total,max_satb_worker_mark_handoff_hash,satb_worker_mark_handoff_failures_total,satb_remark_drained_total,satb_shadow_marked_total,satb_shadow_duplicates_total,satb_shadow_overflow_total,satb_mark_bitmap_segments_total,satb_mark_bitmap_bytes_total,satb_mark_bitmap_apply_segments_total,satb_mark_bitmap_apply_bits_total,satb_mark_bitmap_apply_old_space_bits_total,satb_mark_bitmap_apply_mark_mode_bits_total,satb_mark_bitmap_apply_copy_mode_bits_total,satb_mark_bitmap_apply_extended_old_space_bits_total,satb_mark_bitmap_apply_extended_mark_mode_bits_total,satb_mark_bitmap_apply_extended_copy_mode_bits_total,satb_mark_bitmap_apply_extended_skipped_bits_total,satb_mark_bitmap_apply_skipped_bits_total,satb_mark_bitmap_real_apply_objects_total,satb_mark_bitmap_real_apply_extended_objects_total,satb_mark_bitmap_real_apply_interior_bits_total,satb_mark_bitmap_real_apply_skipped_bits_total,satb_mark_bitmap_real_apply_failures_total,satb_mark_bitmap_copy_reconcile_objects_total,satb_mark_bitmap_copy_reconcile_extended_objects_total,satb_mark_bitmap_copy_reconcile_forwarded_total,satb_mark_bitmap_copy_reconcile_not_forwarded_total,satb_mark_bitmap_copy_reconcile_interior_bits_total,satb_mark_bitmap_copy_reconcile_skipped_bits_total,satb_mark_bitmap_copy_reconcile_failures_total,satb_generated_prewrite_records_total,satb_dirty_card_records_total,satb_dirty_card_heap_refs_total,satb_dirty_card_younger_refs_total,max_aux_sweepers,max_requested_aux_sweepers,max_effective_aux_sweepers,max_sweeper_estimated_work_bytes,max_sweeper_min_work_bytes,max_greedy_assignment,max_remote_consecutive_batch,max_dirty_greedy_assignment,dirty_rebalanced_segments_total,dirty_rebalanced_bytes_total,max_worker_count,max_workers_with_swept_bytes,max_worker_wall_us,max_worker_accum_us,max_worker_step_us,max_worker_swept_bytes,remote_flushes_total,remote_delivery_batches_total,remote_lock_wait_total_us,remote_lock_hold_total_us,max_remote_send_batch,max_remote_delivery_batch,max_remote_receive_depth,remote_sent_total,remote_received_total,request_to_start_min_us,request_to_start_p50_us,request_to_start_p95_us,request_to_start_p99_us,request_to_start_p999_us,request_to_start_avg_us,request_to_start_max_us,collector_min_us,collector_p50_us,collector_p95_us,collector_p99_us,collector_p999_us,collector_avg_us,collector_max_us,setup_p99_us,mark_sweep_p99_us,special_p99_us,rebuild_p99_us,finish_p99_us,dirty_setup_p99_us,guardian_finalizer_p99_us,weak_pair_p99_us,ephemeron_p99_us +contended-mutex,8,default,default,default,default,default,default,default,default,default,5,bsd,0.250000,0.040000,0.010000,20.00,55328,4000.00,8,0,3,2,0,2288,0,37486592,0,637,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1048576,0,0,0,64,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,5,27145,27145,27145,5433.80,27145,19,131,647,647,647,286.60,647,17,603,6,22,2,0,2,1,3 new file mode 100644 --- /dev/null +++ b/docs/gc/gc-improvement.md @@ -0,0 +1,865 @@ +# Chez GC Improvement Handoff + +## Decision + +Do not begin by replacing Chez's collector with a Java-style concurrent collector. The vendored runtime already has a generational, moving collector with a parallel stop-the-world (STW) sweep/copy path. The next implementation should first make that path observable, remove rendezvous tail latency caused by non-cooperating blocking calls, and improve its load balancing. This is the only approach with a credible path to a production result in the next 3-6 months. + +## Measured Results (macOS arm64, 12-thread contended-mutex workload) + +### SAFE_USER_MUTEX + TRAP fix + consecutive remote batch + adaptive sweep stack (this branch) + +| Metric | Before (upstream) | After (this branch) | Improvement | +| --- | --- | --- | --- | +| Rendezvous p50 | 132µs | 27µs | 5x | +| Rendezvous p99 | ~27,000µs | 91-161µs | **170-300x** | +| Rendezvous max | 34,481µs | 102µs | **338x** | +| Events with rendezvous > 1ms | 4/667 (0.6%) | **0/683 (0%)** | eliminated | +| Collector p50 (contended) | 183µs | 148µs | 19% | +| Collector p99 (contended) | 1,450µs | 419-554µs | **62-71%** | +| Total pause p50 (contended) | — | 178µs | sub-millisecond | +| Total pause p99 (contended) | ~27,000µs | 512-667µs | **40-53x** | +| Collector p50 (balanced, 8-thread) | 961µs | 806-868µs | **10-16%** | +| Balanced max_worker_wall p50 | 919µs | 736-816µs | **11-20%** | +| FFI audit | — | 0 blocking-without-collect-safe | Phase-1 complete | + +Four changes deliver this result: +1. **SAFE_USER_MUTEX default**: threads deactivate while blocked on user mutexes, eliminating rendezvous tails from mutex contention. +2. **TRAP=1 in S_fire_collector**: forces the next loop back-edge trap to fire immediately when the collector fires, eliminating tails from threads in tight computation without allocating. The compiler already inserts trap-checks at loop back-edges (`generate-interrupt-trap` defaults to `#t`), but TRAP only decremented on allocation. Setting TRAP=1 forces the next back-edge to check SOMETHINGPENDING and respond immediately. +3. **Consecutive remote batch default**: collapses consecutive top-of-stack remote sends to the same sweeper destination into a single sweep_mutex acquisition, eliminating lock contention spikes during parallel collection. Reduces collector p99 by 62-71%. +4. **Adaptive sweep stack pre-allocation**: pre-allocates sweep stacks during `setup_sweepers` when the collection has >32 segments per sweeper, avoiding mid-sweep `enlarge_stack` calls that acquire the alloc_mutex. Reduces balanced workload collector p50 by 10-16%. + +The balanced workload (8 threads, uniform allocation, ~34MB copied) is at 63-93% of Apple Silicon's peak memory bandwidth (~52GB/s aggregate out of ~68-100GB/s). This is the hardware limit for a copying collector — the remaining gap is from random pointer chasing during scan. Further improvement requires Phase-3B concurrent marking (12-20 month effort). + +### Parallel GC utilization (gc-par path, 12 sweepers) + +| Phase | p50 | Notes | +| --- | --- | --- | +| setup | 14µs | serial, fast | +| mark_sweep | 183µs | parallel, 83% of collector time | +| max_worker_wall | 143µs | well-balanced across 12 sweepers | +| serial overhead | 40µs | mark_sweep - max_worker_wall | +| special | 11µs | ephemeron/guardian/weak fixed points | +| rebuild | 5µs | serial, fast | + +### collect-trip-bytes tuning + +| Trip threshold | Pause p50 | Pause p99 | Segments/collection | Collections | +| --- | --- | --- | --- | --- | +| 8MiB (default) | 204µs | 547µs | 550 | 663 | +| 1MiB | 187µs | 456µs | 101 | 4,271 | + +Smaller trip thresholds reduce individual pause size at the cost of more frequent collections. For latency-sensitive workloads, `(collect-trip-bytes 1048576)` reduces p99 by 17%. For throughput-sensitive workloads, the 8MiB default minimizes total GC overhead. + +### Phase-2A scheduler tuning (opt-in) + +| Configuration | Contended p50 | Contended p99 | Balanced p50 | Notes | +| --- | --- | --- | --- | --- | +| Default | 284µs | 3270µs | 1519µs | safe for all workloads | +| `CHEZ_GC_PARALLEL_SWEEPERS=auto CHEZ_GC_SWEEPER_MIN_WORK_BYTES=auto` | 162µs | 690µs | 2821µs | best for contended, hurts balanced | +| `CHEZ_GC_SWEEPER_ASSIGN=greedy` alone | 238µs | 747µs | — | moderate contended improvement | +| All three combined | 198µs | 1738µs | 2453µs | not recommended as default | + +**Recommendation:** Keep defaults for general use. For mutex-heavy or contended workloads, set `CHEZ_GC_PARALLEL_SWEEPERS=auto CHEZ_GC_SWEEPER_MIN_WORK_BYTES=auto`. The min-work trimming reduces unnecessary sweeper startup when estimated work is small (4 segments per sweeper), but it hurts balanced workloads where all sweepers are needed for large collections. + +## Branch Implementation Status + +Branch `gc-improvement` in `~/mine/jerboa-gc` starts the first implementation ticket: + +- `ENABLE_GC_TELEMETRY` adds allocation-free GC dispatch telemetry around `S_fire_collector`, `S_gc`, and the parallel sweeper teardown. It emits JSON lines only when both compiled with `-DENABLE_GC_TELEMETRY` and run with `CHEZ_GC_TELEMETRY=1`. By default events go to stderr; set `CHEZ_GC_TELEMETRY_FILE=/path/to/events.jsonl` to redirect them to a sidecar file for tests that assert stderr contents. +- The telemetry event includes dispatch path, rendezvous entry latency, total collector time, coarse phase times (`setup_us`, `mark_sweep_us`, `special_us`, `rebuild_us`, `finish_us`), selected special-work subphase times (`dirty_setup_us`, `guardian_finalizer_us`, `weak_pair_us`, `ephemeron_us`), heap bytes before/after, copy-versus-mark source segment counts and byte estimates, worker count, swept bytes, and remote handoff counts. +- The event also includes coarse parallel-worker imbalance fields: `max_worker_wall_us`, `max_worker_accum_us`, `max_worker_step_us`, `max_worker_swept_bytes`, and `workers_with_swept_bytes`. `max_worker_wall_us` is available in telemetry builds; the `*_accum_us` and `*_step_us` fields remain tied to Chez's `ENABLE_TIMING` counters. +- Remote-handoff telemetry now reports `remote_flushes`, `remote_delivery_batches`, `remote_lock_wait_us`, `remote_lock_hold_us`, `max_remote_send_batch`, `max_remote_delivery_batch`, and `max_remote_receive_depth`. Use these before changing the remote protocol: high wait time points to mutex contention, high hold time with large batches points to copy/drain cost, and a large gap between send batches and delivery batches shows whether batching is actually collapsing work. +- `scripts/gc-baseline.sh` runs Phase-0 allocation/thread workloads and saves raw telemetry JSONL plus stdout/stderr under `build/gc-baseline`. +- The baseline driver records normalized per-run resource metrics beside each telemetry log: wall seconds, user/sys seconds, CPU percentage, max RSS, iterations/sec, and the benchmark result. It uses `/usr/bin/time -l`, `/usr/bin/time -v`, or `/usr/bin/time -p` when available, and falls back to wall time if external resource metrics are unavailable. Set `GC_BASELINE_RESOURCE_METRICS=0` to use the fallback path deliberately. +- The default workload list includes `locked-immobile`, `special-reachability`, `blocking-io`, `contended-mutex`, and `condition-wait` coverage in addition to `cross-owner-graph`, which keeps owner threads alive after allocating per-thread node vectors, cross-links those owner-local objects, and then allocates again. Use `cross-owner-graph` to stress the parallel collector's remote-rescan path. Use `blocking-io` to keep a collect-safe native `read` blocked while allocator threads force GC rendezvous. Use `condition-wait` and `contended-mutex` to isolate user-level blocking synchronization tails. +- `scripts/gc-baseline.sh` accepts `GC_BASELINE_SWEEPER_CAPS='default 1 2 4'` to run a sweeper-policy matrix in one pass. +- `scripts/gc-baseline.sh` accepts `GC_BASELINE_SWEEPER_MIN_WORKS='default auto 65536'` to run the useful-work worker-trimming policy in one pass. +- `scripts/gc-baseline.sh` accepts `GC_BASELINE_ASSIGN_POLICIES='default greedy'` to compare the current round-robin context assignment with the new opt-in score-based assignment. +- `scripts/gc-baseline.sh` accepts `GC_BASELINE_DIRTY_ASSIGNMENTS='default greedy'` to compare default dirty-segment ownership with the new opt-in whole-segment dirty work redistribution. The implementation keeps dirty segments owned by active allocator threads on their original `thread_gc`, because dirty sweeping uses that owner for allocation-bound checks. +- `scripts/gc-baseline.sh` accepts `GC_BASELINE_POLICY_PRESETS='default phase2a descriptor-split phase2a-split region-observe region-select region-evacuate mark-satb mark-queue mark-concurrent mark-apply phase3-observe'` as a compact alternative to the full Cartesian policy matrix. Supported presets are `default`, `phase2a`, `sweeper-auto`, `useful-work`, `greedy`, `dirty-greedy`, `remote-consecutive`, `descriptor-observe`, `descriptor-split`, `phase2a-split`, `region-observe`, `region-select`, `region-evacuate`/`partial-evacuate`, `mark-satb`/`satb-observe`, `mark-queue`/`satb-queue`, `mark-concurrent`/`concurrent-observe`, `mark-apply`/`concurrent-apply`, and `phase3-observe`. Presets are intended for smoke runs and decision-gate evidence where the all-default row and one or more named candidates must be present without exploding local runtime. `phase3-observe` combines the Phase-2A scheduler knobs with region evacuation preflight and the concurrent-mark observer so a single row exercises the current debug stack before production marker workers or real partial evacuation exist. +- `scripts/gc-baseline.sh` writes `skipped.csv` beside `index.csv` for intentionally excluded proof-matrix rows. By default it skips `extended-mark-objects` with `region_experiment=evacuate`, because that mix can abort before the later `mark-apply` row records extended-object SATB bitmap evidence. Set `GC_BASELINE_ALLOW_EXTENDED_REGION_EVACUATE=1` only for focused region-root or layout probes that deliberately exercise this unsupported combination. The ordinary `extended-mark-objects` workload also keeps port objects disabled unless `GC_EXTENDED_MARK_INCLUDE_PORTS=1`, so closed-port movement remains an opt-in focused selector probe rather than a broad mark-barrier default. +- `scripts/gc-baseline.sh` accepts `GC_BASELINE_COLLECT_TRIP_BYTES='default 32768 8388608'` to run the workload suite under multiple Chez `collect-trip-bytes` thresholds. Use a small fixed value to force frequent collections and a larger value to model a more provisioned nursery/heap cadence. The summary, comparison, attribution, and throughput CSVs carry this as `collect_trip_bytes`. +- `CHEZ_GC_DESCRIPTOR_EXPERIMENT=observe` enables the debug-only Phase-2B observer. It does not split work or change ownership. It records the source segments that could become immutable descriptors and the largest original-owner share of that source work (`descriptor_source_*`, `descriptor_max_owner_*`) so the one-allocator gate can tell whether dynamic segment splitting is worth the risk. +- `CHEZ_GC_DESCRIPTOR_EXPERIMENT=split` enables the first debug-only Phase-2B split prototype in telemetry builds. It activates only after roots, symbols, protected C pointers, and dirty setup are handled. It reassigns whole eligible copy-mode source segments from the dominant original owner to already-started sweeper `thread_gc`s; it excludes mark-in-place segments, marked/locked segments, weak/ephemeron/trigger-bearing segments, code/continuation/data-only spaces, and every non-telemetry build. Telemetry records `descriptor_split_segments` and `descriptor_split_bytes`. +- `CHEZ_GC_REGION_EXPERIMENT=observe` enables the debug-only Phase-3A region remembered-set observer in telemetry builds. It records pointer-store barrier traffic from `S_dirty_set`: total heap pointer stores, cross-segment stores, cross-generation stores, old-to-young stores, old-to-old cross-segment stores, bounded distinct old-to-old remembered-set region edges, and edge-table overflow. It does not change GC behavior. +- `CHEZ_GC_REGION_EXPERIMENT=select` keeps the observer enabled and also runs a debug-only collection-set selector during GC setup. The selector computes eligible copy-mode source-region candidates and the subset that fits `CHEZ_GC_REGION_PAUSE_BUDGET_BYTES` (`auto`/unset = four Chez segments). It records candidate/selected/excluded region counts and bytes plus conservative live-byte, scan-cost, and evacuation-cost estimates without performing partial evacuation. Until production concurrent mark data exists, selected-region live and evacuation estimates default to whole-segment cost when prior marked-byte density is not available. +- `CHEZ_GC_REGION_EXPERIMENT=evacuate`/`partial-evacuate` keeps the observer and selector enabled and adds a debug-only partial-evacuation preflight. The preflight uses the old-to-old remembered-set edge table captured before GC begin, checks whether selected regions have inbound edges from non-selected regions, and reports `region_evacuation_*` counters: selected, eligible, blocked, inbound remembered-set edges, selected-to-selected internal edges, selected-to-nonselected outbound edges, total remembered-set update edges, table overflow, selected evacuation bytes required, empty-segment reserve bytes, reserve deficit bytes, reserve failures, and evacuation failure-policy events. The failure-policy counters classify nonzero evacuation requirements as reserve-ok or fallback-required and keep policy deficit bytes separate from the raw reserve preflight. `CHEZ_GC_REGION_RESERVE_LIMIT_BYTES=N` caps the reported reserve for deterministic stress of the reserve-deficit path; when fallback is required, `region_evacuation_fallback_suppressed_*` records the selected source/target/copy sizing and suppresses target reservation, allocator/map, object, copy, and forwarding work for that telemetry event so the cycle falls back to ordinary STW collection instead of reporting a half-plan. The fallback branch also records a conservative whole-segment selected-set shrink proof through `region_evacuation_shrink_*`, so a deficit row shows whether a smaller eligible prefix would fit the observed reserve before the telemetry path falls back. It also emits a conservative selected-set relocation plan with `region_relocation_plan_*` counters: source segments, destination target segments, copy bytes, reference-update entries, metadata bytes, and plan failures. The plan separately inventories ordinary heap empty segments from the normal `S_chunks` pool with `region_relocation_target_*`, so target readiness is not confused with aggregate empty-space accounting, leases the sampled ordinary heap targets out of `chunk->unused_segs` until telemetry finish with `region_relocation_target_reserved_*` and reservation/unreserve failure counters, prepares bounded bump-allocation cursors over the leased targets with `region_relocation_allocator_*`, records a bounded source-to-target reservation map contract with `region_relocation_map_*`, runs an object-copy preflight with `region_relocation_object_preflight_*`, places selected objects into leased target cursors with `region_relocation_copy_schedule_*` while grouping by publishable target `space` and target `generation`, emits source/destination/size relocation-entry metadata with `region_relocation_copy_entry_*`, copies the selected object bytes from physical object starts into the detached target segments with `region_relocation_copy_bytes_*`, records a bounded source-to-destination forwarding table with logical and physical copy ranges plus target space/generation/index metadata through `region_relocation_forwarding_*`, runs an opt-in reversible pair/symbol source forwarding-marker proof with `CHEZ_GC_REGION_FORWARDING_MARKER=1` and `region_relocation_forwarding_marker_*`, applies exact selected-target slot rewrites inside those detached copied objects with `region_relocation_copy_slot_rewrite_*`, runs a reversible target-publication metadata preflight with `region_relocation_target_publish_*`, runs an opt-in reversible occupied-list/accounting publication proof with `CHEZ_GC_REGION_TARGET_LINK=1` and `region_relocation_target_link_*`, runs a conservative remembered-edge rewrite preflight with `region_relocation_rewrite_*`, runs an exact selected-object slot rewrite inventory with `region_relocation_slot_rewrite_*`, and runs an exact non-selected dirty-card slot rewrite preflight with `region_relocation_external_slot_rewrite_*`. The forwarding lookup now accepts exact object starts and interior addresses within copied source ranges, which is required before exact slot rewriting can preserve interior-pointer offsets. Region selection now uses a stricter movable-space policy than descriptor splitting: `space_port` and `space_code` are explicitly excluded from the selected evacuation set until movement and reference rewriting have production support for those spaces. The selector also rejects dynamic-layout and closure segments unless the object walk starts on the segment boundary and every object ends inside the same segment, so spanning or ambiguous object groups are excluded before target planning instead of being discovered only by object preflight. Selector telemetry breaks excluded segments into mark-state, special-reachability, unsupported-space, and ambiguous-layout buckets through `region_excluded_mark_segments`, `region_excluded_special_segments`, `region_excluded_space_segments`, and `region_excluded_layout_segments`; use those counters to size the weak/ephemeron/finalizer and generated-layout integration work before broadening the movable collection set. The stress/gate scripts reject missing or inconsistent evacuation failure-policy evidence, fallback suppression and selected-set shrink evidence when the reserve is deliberately capped with `GC_REGION_REQUIRE_FALLBACK=1`, selected-set object-preflight exclusions, target allocator cursor failures, copy-schedule placement failures, copy-entry metadata mismatches, byte-copy mismatches, forwarding-table coverage mismatches, forwarding-marker install/restore mismatches when required with `GC_REGION_REQUIRE_FORWARDING_MARKER=1`, detached-copy slot-rewrite mismatches, target-publication metadata mismatches, target-link list/accounting mismatches when required with `GC_REGION_REQUIRE_TARGET_LINK=1`, remembered-edge rewrite-preflight mismatches, exact selected-object slot forwarding mismatches, exact external dirty-card slot forwarding mismatches, and reversible live-slot apply/restore mismatches when required with `GC_REGION_REQUIRE_LIVE_SLOT_REWRITE=1`, and deferred production-order live-slot rehearsal mismatches when required with `GC_REGION_REQUIRE_LIVE_SLOT_COMMIT_ORDER=1`. The `region-update-edges` workload creates old-to-old pair-link stores, and `GC_REGION_REQUIRE_UPDATE_EDGES=1` now requires either coarse selected-set remembered-edge rewrite evidence or exact external dirty-slot selected-target forwarding evidence. Coarse edge-table overlap can still be zero on small runs, but the exact dirty-slot scanner provides deterministic selected-target update coverage. It writes copied bytes only into detached ordinary-heap target segments that remain unreachable and are released at telemetry finish; the target-publication preflight temporarily assigns destination `seginfo` `space`, `generation`, `old_space`, `use_marks`, `must_mark`, and dirty metadata checks, `CHEZ_GC_REGION_TARGET_LINK=1` can temporarily link those targets into `S_G.occupied_segments` and charge/restore `bytes_of_space`, `bytes_of_generation`, and `number_of_nonstatic_segments`, and the release path still restores targets to empty. `CHEZ_GC_REGION_FORWARDING_MARKER=1` can temporarily install Chez forwarding marker/address words on pair-sized and symbol copied source starts and restore the original words; a generic arbitrary-header marker probe crashed and is recorded as an anti-pattern. `CHEZ_GC_REGION_LIVE_SLOT_REWRITE=1` can temporarily write forwarded addresses into exact live selected-source and external dirty-card slots and then restore the original values before target release. `CHEZ_GC_REGION_LIVE_SLOT_COMMIT_ORDER=1` defers that temporary live-slot write/restore until after target-publication, occupied-list/accounting target-link, and source forwarding-marker proofs have run, matching the production update order while still restoring for telemetry safety. The forwarding, rewrite, and publication tables remain telemetry-only metadata and do not install durable production forwarding markers or keep copied targets live after telemetry finish. The exact selected-object slot inventory now scans pair-like spaces, symbols, fixed-layout port fields, closure environments, record pointer-mask fields, vector/any-stencil-vector entries, boxes, TLCs, ratnum/exactnum pointer fields, reference-array entries, and scalar pointer-free typed objects as exact zero-slot layouts (`string`, `fxvector`, `flvector`, plain `bytevector`, `inexactnum`, and `bignum`), while still deferring unsupported typed-object layouts explicitly and breaking selected-object deferrals down through `region_relocation_slot_rewrite_deferred_typed_objects` and `region_relocation_slot_rewrite_deferred_space_objects`. The exact external dirty-card preflight scans non-selected dirty cards for pair-like, symbol, weak-pair, reference-array, port, unmarked impure-record, closure, and supported dynamic typed-object slots with the same scalar and pure-number typed-object coverage; ambiguous spanning dynamic cards, marked dynamic cards, unsupported typed-object layouts, and special-reachability ephemeron cards are reported as deferred cards and broken down through `region_relocation_external_slot_rewrite_deferred_marked_cards`, `region_relocation_external_slot_rewrite_deferred_ambiguous_cards`, `region_relocation_external_slot_rewrite_deferred_unsupported_cards`, and `region_relocation_external_slot_rewrite_deferred_special_cards`. +- `CHEZ_GC_REGION_SOURCE_RETIRE=1` enables a debug-only source-retirement readiness proof after target publication, target-link accounting, forwarding-marker proof, and production-order live-slot rewrite rehearsal. It records `region_relocation_source_retire_*` counters for selected source segments with forwarding coverage, forwarded object/byte coverage, publishable target segments, commit-ordered rewrite slots, a structural hash, and failures. This does not yet retire sources or retain targets durably; it proves the current event has the prerequisites for the next one-way rewrite/target-retention implementation slice. +- `CHEZ_GC_REGION_SOURCE_RETIRE_PROOF=1` strengthens the source-retirement gate with a reversible stopped-world empty/restore proof over copied selected-source segments. It records `region_relocation_source_retire_proof_*` counters for selected source segments, forwarded bytes, temporary empty-list transitions, restored segments, nonstatic/empty/chunk segment deltas, heap-byte delta, a structural hash, and failures. The proof temporarily models Chez's copied-source retirement path by moving selected source `seginfo`s to `space_empty`, pushing them onto their chunk unused lists, adjusting `number_of_nonstatic_segments`, `number_of_empty_segments`, and chunk `nused_segs`, then restoring every pointer and counter before the collector continues. It is still a proof, not real source retirement. +- `CHEZ_GC_REGION_ONE_WAY_REWRITE_PROOF=1` adds a stricter stopped-world live-slot rewrite proof after source-retire proof and cumulative target-retain proof. It records `region_relocation_one_way_rewrite_*` counters for selected-source slots and selected external dirty-card slots that can be written to forwarded target addresses, verified while left forwarded, and then restored only as telemetry cleanup. This separates the would-be production forward-only rewrite phase from the restore step; it still does not leave slots forwarded after telemetry finish because copied targets are not yet durable heap residents. +- `CHEZ_GC_REGION_TARGET_RETENTION=1` enables a debug-only target-retention handoff gate after source-retirement readiness and before the production-commit gate. It records `region_relocation_target_retention_*` counters for requested, ready, and blocked events, missing prerequisite counts, a blocker mask, and the three target-specific durable-production blockers: copied targets are still released at telemetry finish, the target-link proof still unlinks targets immediately, and heap/space/generation accounting is still restored rather than committed. This gate intentionally does not keep targets live; it isolates the target-retention work from generated-copy and source-sweep integration. +- `CHEZ_GC_REGION_TARGET_RETAIN_PROOF=1` strengthens the target-retention gate with a cumulative stopped-world link/accounting proof over the full copied-target set. It records `region_relocation_target_retain_proof_*` counters for target segments, copied bytes, linked/unlinked segment counts, accounting bytes, heap-byte delta, a structural hash, and failures. Unlike the earlier target-link proof, this links all prepared target segments at once and verifies aggregate heap accounting before restoring the list/accounting state and leaving the existing telemetry release path intact. It is still a proof, not durable retention. +- `CHEZ_GC_REGION_TARGET_RELEASE_SUPPRESSION_PROOF=1` adds the next target-ownership handoff proof after the cumulative target-retain proof, one-way slot rewrite proof, and production-commit prerequisite gate. It records `region_relocation_target_release_suppression_*` counters for requested/ready events, retained target segments and bytes captured from the same event, retained target segments and bytes that bypass the ordinary telemetry-finish release branch, explicit cleanup-release segments and bytes, prerequisite failures, a structural hash, and failures. This proves the copied-target set can be carried across the normal release point as one owned set, and the strict wrapper now checks the event-local retained counts instead of comparing against workload-level retained-target aggregates. It still performs explicit cleanup before emitting telemetry, so durable target ownership and cleanup-release removal remain future work. +- `CHEZ_GC_REGION_TARGET_LIVE_THROUGH_EMIT_PROOF=1` moves the target-ownership proof one step later: after release suppression succeeds, copied targets are linked into occupied segment lists and charged to heap accounting for the heap snapshot emitted in telemetry, then unlinked and released before GC returns to the mutator. It records `region_relocation_target_live_emit_*` counters for requested/ready events, linked target segments/bytes, heap-byte delta, cleanup unlink/release segments and bytes, prerequisite failures, a structural hash, and failures. This proves the target set can be represented as live heap residents through the final telemetry snapshot; it still does not leave them linked after GC return. +- `CHEZ_GC_REGION_SLOT_LIVE_THROUGH_EMIT_PROOF=1` extends the target-live-through-emit proof by saving the current selected-source and external dirty-card slot values at telemetry finish, writing their forwarded copied-target destinations while retained targets are linked/accounted for the final heap snapshot, verifying the forwarded slot window, and then restoring those saved current values before target cleanup. It records `region_relocation_slot_live_emit_*` counters for selected/applied/verified/restored source slots, external slots, prerequisite failures, a structural hash, and failures. This proves the final emitted heap snapshot can overlap live forwarded references with live copied targets; it still restores slots before GC returns. +- `CHEZ_GC_REGION_SOURCE_LIVE_THROUGH_EMIT_PROOF=1` extends that final-snapshot proof to selected source ownership. After retained targets are live/accounted and selected source/external slots are forwarded, it uses the preserved source-retire segment list to verify selected source segments are empty or can be moved to `space_empty` through the telemetry heap snapshot, then restores saved source metadata before restoring slots and targets. It records `region_relocation_source_live_emit_*` counters for requested/ready events, emptied source segments and bytes, nonstatic/empty/chunk deltas, heap delta, cleanup restored segments/bytes, prerequisite failures, a structural hash, and failures. This proves target-live and slot-forwarded evidence can overlap with source-retired evidence at the final snapshot; it still performs cleanup before GC returns, so the production source-restore and source-sweep blockers remain. +- `CHEZ_GC_REGION_SOURCE_SWEEP_ACCOUNTING_PROOF=1` adds a stricter source-sweep accounting proof on top of source-live-through-emit. While retained targets are live/accounted, slots are forwarded, and selected source segments are empty in the final snapshot window, it records `region_relocation_source_sweep_accounting_*` counters for requested/ready events, retired source segments/bytes, nonstatic/empty/chunk deltas, heap delta, remaining restore-cleanup and sweep-integration-missing events, prerequisite failures, a structural hash, and failures. This proves the selected-source accounting state is visible at the final snapshot and makes the two remaining source blockers explicit; it still does not integrate with Chez's real sweep path or return from GC with sources retired. +- `CHEZ_GC_REGION_PRODUCTION_COMMIT=1` enables a debug-only production-commit handoff gate after source-retirement and target-retention readiness. It records `region_relocation_production_commit_*` counters for requested, ready, and blocked events, missing prerequisite counts, a blocker mask, and the aggregate durable-production blockers: copied targets are still released at telemetry finish, forwarded source/external slots are still restored before GC return, selected-source retirement is not integrated with Chez's sweep/accounting path, object copying/forwarding is still a wrapper observer rather than the generated collector's production forwarding path, and root forwarding coverage is still incomplete. The target-release aggregate is split into `region_relocation_production_commit_target_release_cleanup_blockers`, `region_relocation_production_commit_target_unlink_cleanup_blockers`, `region_relocation_production_commit_target_accounting_restore_blockers`, and `region_relocation_production_commit_target_release_blocker_mask`, so the gate distinguishes explicit cleanup release, target unlink cleanup, and heap/space/generation accounting restore debt from durable copied-target ownership. The slot-restore aggregate is split into `region_relocation_production_commit_selected_slot_restore_blockers`, `region_relocation_production_commit_external_slot_restore_blockers`, and `region_relocation_production_commit_slot_restore_blocker_mask`, so the gate distinguishes selected-source slot restoration from external dirty-card slot restoration before durable forwarding slots are left in the heap. The generated-copy aggregate is split into `region_relocation_production_commit_wrapper_copy_blockers`, `region_relocation_production_commit_wrapper_forwarding_blockers`, `region_relocation_production_commit_marker_layout_blockers`, and `region_relocation_production_commit_generated_copy_blocker_mask`, so the gate distinguishes detached wrapper-owned byte copying, wrapper-owned forwarding metadata, and layout-limited source-marker proof from future generated-collector ownership. The selected-source aggregate is also split into `region_relocation_production_commit_source_restore_blockers` for the reversible empty/restore cleanup that must disappear before GC return and `region_relocation_production_commit_source_sweep_blockers` for the missing real sweep/accounting integration. The source-restore aggregate is further split into `region_relocation_production_commit_source_metadata_restore_blockers`, `region_relocation_production_commit_source_nonstatic_restore_blockers`, `region_relocation_production_commit_source_empty_restore_blockers`, `region_relocation_production_commit_source_chunk_restore_blockers`, and `region_relocation_production_commit_source_restore_blocker_mask`, so the gate distinguishes source metadata restoration from nonstatic-segment, empty-list, and chunk-accounting restoration. The source-sweep aggregate is split into `region_relocation_production_commit_source_restore_cleanup_blockers`, `region_relocation_production_commit_source_sweep_integration_blockers`, and `region_relocation_production_commit_source_sweep_blocker_mask`, so the gate distinguishes telemetry cleanup that restores selected sources from the missing integration with Chez's real sweep/accounting retire path. The root-coverage aggregate is split into `region_relocation_production_commit_thread_root_blockers`, `region_relocation_production_commit_thread_list_root_blockers`, `region_relocation_production_commit_thread_stack_root_blockers`, `region_relocation_production_commit_thread_context_root_blockers`, `region_relocation_production_commit_thread_frame_root_blockers`, `region_relocation_production_commit_thread_virtual_register_root_blockers`, `region_relocation_production_commit_thread_root_blocker_mask`, `region_relocation_production_commit_thread_stack_root_blocker_mask`, `region_relocation_production_commit_protected_root_blockers`, `region_relocation_production_commit_oblist_root_blockers`, `region_relocation_production_commit_count_root_blockers`, `region_relocation_production_commit_count_root_collection_blockers`, `region_relocation_production_commit_object_count_collection_blockers`, `region_relocation_production_commit_backreference_collection_blockers`, `region_relocation_production_commit_count_root_blocker_mask`, `region_relocation_production_commit_count_root_excluded_events`, and `region_relocation_production_commit_root_coverage_blocker_mask`, so the gate distinguishes Scheme thread-list roots, generated thread-context root fields, stack-frame roots, virtual registers, protected C pointer cells, interned-symbol oblist roots, actual count-root, object-count, and backreference dispatch rows, the reason-specific count-root blocker mask, and ordinary region relocation rows that prove those count-root entry paths are excluded. The selector-exclusion aggregate is split into `region_relocation_production_commit_mark_selector_blockers`, `region_relocation_production_commit_special_selector_blockers`, `region_relocation_production_commit_space_selector_blockers`, `region_relocation_production_commit_layout_selector_blockers`, and `region_relocation_production_commit_selector_exclusion_blocker_mask`, so the gate distinguishes mark-state, special-reachability, unsupported-space, and ambiguous-layout coverage gaps before broadening the movable set. This gate intentionally does not keep targets live, leave slots forwarded, leave roots forwarded, run relocation on count-root collections, or retire sources; it prevents a future implementation from mistaking the reversible proof for a production commit. +- `CHEZ_GC_REGION_ROOT_FIRST=1` adds a telemetry-only selector bias for old ordinary regions referenced by scanned root surfaces. It uses the same protected-cell, oblist, thread-list, thread-context, virtual-register, and return-address live-mask stack-frame scanner as the root proof, runs after typed-object selection and before the ordinary fallback selector, and records `region_root_candidate_segments`, `region_root_selected_segments`, and `region_root_layout_excluded_segments`. +- `CHEZ_GC_REGION_ROOT_REWRITE_PROOF=1` adds the opt-in root forwarding proof for protected C pointer cells, oblist bucket symbols, and the Scheme thread-list surface. After target-live, slot-live, source-live, and source-sweep accounting proofs are all live in the final snapshot window, it scans `S_G.protected[]`, every `S_G.oblist` bucket, the global `S_threads` list root, each thread-list `cdr`, each thread-list `car` thread-object reference, the 21 generated `sweep_thread` thread-context root fields, every virtual register slot in each thread context, and the stack-frame live slots described by Chez return-address live masks. It temporarily forwards protected pointer-cell values, bucket symbols, thread-list links, thread-object references, thread-context root fields, virtual-register values, and stack-frame live slots whose referents are selected source objects, verifies the written destination, restores the original value, and records `region_relocation_root_rewrite_*` counters for requested/ready events, protected slot coverage, oblist bucket coverage, thread-list root coverage, thread-object root coverage, thread-context stack-root inventory, frame-root selected/applied/verified/restored counts, context-root selected/applied/verified/restored counts, virtual-register selected/applied/verified/restored counts, prerequisite failures, a structural hash, and failures. It also runs an earlier reversible `region_relocation_root_probe_*` proof immediately after the copy schedule builds the forwarding table, before the final target/source live-through window can hide selected roots. The early probe writes forwarded destinations into scanned root cells, verifies them, restores the originals, and records aggregate requested/ready/slot/selected/applied/verified/restored/hash/failure counters. When the final proof is clean it clears the protected-root, oblist-root, thread-list-root, thread-context-root, stack-frame-root, and virtual-register-root sub-blockers for that same final-snapshot row; if all thread sub-blockers clear, the thread/root-coverage production masks clear too. `scripts/gc-region-stress.sh` now includes the focused `region-rooted-stack` workload by default and accepts `GC_REGION_REQUIRE_SELECTED_ROOT_REWRITE=1`, which fails unless either final-snapshot selected root counters are clean or the early root-forwarding probe has nonzero selected slots with selected/applied/verified/restored counts equal and zero failures. It does not run on count-root/object-count/backreference collections or return from GC with protected/oblist/thread/thread-context/stack roots left forwarded. +- `CHEZ_GC_MARK_EXPERIMENT=satb-observe` enables the debug-only Phase-3B SATB pre-write observer in telemetry builds. It records overwritten heap references seen by runtime `S_dirty_set` and generated `build-dirty-store` mutation paths, queue-candidate nonstatic old references, static old references, old-to-younger observations, and new heap references. It does not mark concurrently and does not change collector behavior; it is the coverage gate before adding a concurrent marker. +- `CHEZ_GC_MARK_EXPERIMENT=queue`/`satb-queue` keeps the observer enabled and also enqueues overwritten nonstatic heap references through fixed per-thread SATB candidate buffers into a bounded retained candidate queue. `CHEZ_GC_MARK_THREAD_BUFFER_CAPACITY` caps each mutator buffer up to the compiled maximum, and `CHEZ_GC_MARK_QUEUE_CAPACITY` caps the global queue. Telemetry records configured capacities, buffered candidates, flushes, overflows, enqueued, and drained counts. Overflow is a hard failure for relying on the queue evidence. +- `CHEZ_GC_MARK_EXPERIMENT=concurrent-observe`/`concurrent` adds the debug-only Phase-3C handoff observer. It drains retained SATB candidates through the concurrent-observer drain path and through the STW remark drain at GC begin, recording `satb_concurrent_mark_*`, `satb_remark_drained`, compatibility `satb_shadow_*` counters, and explicit `satb_mark_bitmap_*` counters in an epoch-tagged segment-indexed mark bitmap. After the collector establishes the current `old_space` and copy-versus-mark segment decisions, it reconciles the just-drained bitmap with collector segment state and reports `satb_mark_bitmap_apply_*`: bitmap segments/bits seen, bits on collected old-space segments, bits on mark-mode segments, bits on copy-mode segments, and skipped bits outside the current collection. Threaded telemetry builds start a detached observer worker by default; `CHEZ_GC_MARK_WORKER=0` disables it and uses the synchronous fallback path. `CHEZ_GC_MARK_WORKER_BITMAP_SCAN=1` makes that worker scan the current concurrent bitmap epoch after each asynchronous candidate drain and record `satb_worker_bitmap_scan_*` counters for scan events, segments, bits, and empty/skipped scans; this proves worker-owned bitmap traversal outside the GC pause, but it still does not call Chez's generated marker from the background thread. `CHEZ_GC_MARK_WORKER_HANDOFF_PROOF=1` adds a stricter telemetry proof that ties cumulative worker-owned bitmap scan evidence to a representative STW generated mark-apply/copy-reconciliation event through `satb_worker_mark_handoff_*` counters, while also reporting `satb_worker_mark_handoff_generated_traversal_missing_events` and the sub-blocker counters `satb_worker_mark_handoff_generated_outside_gc_missing_events`, `satb_worker_mark_handoff_generated_thread_gc_missing_events`, `satb_worker_mark_handoff_generated_special_missing_events`, and `satb_worker_mark_handoff_generated_blocker_mask`. Those fields keep the remaining worker-side generated traversal blockers explicit: generated traversal still runs in the GC-begin proof window, the background worker has no generated-marker `thread_gc` owner, and special-reachability rows still require STW fixed-point semantics. The GC-begin path suspends the observer worker before flushing buffers, performing the remark drain, snapshotting counters, preserving the just-drained bitmap epoch for collector-state preflight, and logically resetting the bitmap with an epoch bump, then resumes the worker. +- `CHEZ_GC_MARK_EXPERIMENT=apply`/`mark-apply`/`concurrent-apply` enables the first opt-in mark-state mutation prototype in telemetry builds. It runs only in the parallel GC inclusion after old-space and `use_marks` decisions are established, iterates the preserved SATB bitmap epoch once per mapped sweeper owner context, and calls Chez's generated `mark_object` only for owner-local mark-mode object starts. It now uses object-start synchronization for dynamic typed-object spaces and can apply pair, symbol, port, closure, continuation, typed-object, record, code, and reference-array entries while still excluding weak/ephemeron spaces and every non-old-space/non-owner mark-mode entry. For copy-mode entries, it now runs a telemetry-only opt-in relocation prototype before sweeping and later reconciles those bitmap entries against forwarding state, reporting `satb_mark_bitmap_copy_reconcile_*` counters for copy objects, extended copy objects, forwarded/not-forwarded entries, interior bits, skipped bits, and failures. Telemetry also reports `satb_mark_bitmap_real_apply_*` counters for applied mark-mode objects, extended-space applied objects, interior bitmap bits, skipped bitmap bits, and failures. The default extended stress workload keeps port objects disabled because the full mark wrapper remains sensitive to later `region-evacuate` rows with live ports; set `GC_EXTENDED_MARK_INCLUDE_PORTS=1` only for focused closed-port selector probes. It still does not have production marker workers, does not rewrite arbitrary references from the SATB bitmap outside Chez's existing forwarding path, does not handle weak/ephemeron/finalizer semantics concurrently, and is not a production concurrent marker implementation. +- The same mark experiment also records generated/runtime dirty-card records that arrive through `S_record_new_dirty_card`. Those are post-write records: they prove a store path exists, but they do not provide the overwritten value needed by SATB. Generated dirty stores now call a pre-write slot hook before the assignment and report `satb_generated_prewrite_records`; treat dirty-card traffic with zero generated pre-write records as a hard barrier coverage bug before enabling a real concurrent marker. +- Threaded baseline workloads call `thread-preserve-ownership!` by default so they reliably exercise the existing `gc-par` dispatch gate. Set `GC_BASELINE_PRESERVE_OWNERSHIP=0` to measure unpreserved ownership behavior. +- `scripts/gc-baseline-summary.sh` summarizes baseline JSONL files into p50/p95/p99/p99.9/max CSV and includes collect-trip setting, per-run resource metrics, dispatch counts, phase p99s, special-work subphase times, copy-versus-mark source segment totals, Phase-2B descriptor observer/split fields, Phase-3A region observer/selector/preflight fields, full relocation proof fields through source-live-through-emit, source-sweep accounting, and production-commit blocker evidence, Phase-3B/3C SATB/worker/bitmap/mark-apply fields, remote handoff totals, worker counts, and worker-imbalance columns. It caches per-JSONL numeric `sum`/`max` reductions so large GC summary matrices do not rescan every telemetry file for every output column. +- `scripts/gc-baseline-compare.sh` compares each summary row with the same workload/thread-count/collect-trip `default/default/default/default/default` policy row and emits p99 deltas plus a decision (`candidate-win`, `regress-request-p99`, `regress-collector-p99`, `regress-single-thread-p99`, `remote-batch-effective-no-p99-win`, `insufficient-samples`, `neutral`, or `no-baseline`). It uses both percentage and absolute-microsecond thresholds for regressions, and it requires enough samples before declaring a p99 win or regression. It is a triage tool, not a throughput substitute. +- `scripts/gc-pause-attribution.sh` converts a summary CSV into the first-ticket dominant-contributor report. It labels rows as `rendezvous`, `serial-special-work`, `worker-imbalance`, `remote-queue-contention`, `mark-sweep-memory-bandwidth`, or the largest remaining collector phase. +- `scripts/gc-throughput.sh` runs the same policy and collect-trip dimensions without telemetry and writes raw, summary, and comparison CSV files for wall-clock throughput. Use it for the Phase-2A single-thread regression gate; by default it flags single-thread throughput losses worse than 3%. It accepts `GC_THROUGHPUT_POLICY_PRESETS` with the same preset names as `GC_BASELINE_POLICY_PRESETS`. +- `scripts/gc-decision-gate.sh` turns the summary, comparison, attribution, and throughput comparison CSVs into a single gate report. It emits `pass`, `defer`, or `fail` for Phase 0 attribution, Phase 1 rendezvous regression, Phase 2A policy acceptance, Phase 2B descriptor-experiment readiness, and Phase 3 concurrent-mark readiness. Phase 3 now distinguishes the region relocation proof chain through source-retire, one-way rewrite, target retention, target live-through-emit, slot live-through-emit, source live-through-emit, source-sweep accounting, and production-commit blockers, plus the SATB/worker/bitmap/worker-handoff/mark-apply evidence needed before concurrent marker work. By default it records fail rows without exiting nonzero; set `GC_DECISION_FAIL_ON_FAIL=1` for CI-style enforcement. +- `scripts/gc-ffi-audit.sh` inventories `foreign-procedure` declarations and whether they are marked `__collect_safe`. +- `make gc-baseline`, `make gc-baseline-summary`, `make gc-baseline-compare`, `make gc-pause-attribution`, `make gc-throughput`, `make gc-rendezvous-watchdog`, `make gc-correctness-stress`, `make gc-special-stress`, `make gc-shadow-reachability`, `make gc-region-stress`, `make gc-mark-barrier-stress`, `make gc-satb-barrier-audit`, `make gc-sanitizer-smoke`, `make gc-qualification`, `make gc-mats`, and `make gc-ffi-audit` provide stable entry points for those scripts. +- `scripts/gc-rendezvous-watchdog.sh` runs the Phase-1 blocking rendezvous watchdog cases. It fails if a blocked collect-safe I/O reader, `CHEZ_GC_SAFE_USER_MUTEX=1` contended mutex workload, or condition-wait workload hangs, produces no telemetry, or exceeds `GC_WATCHDOG_MAX_REQUEST_US` request-to-collector-start latency. Set `GC_WATCHDOG_TIMEOUT_SECONDS`, `GC_WATCHDOG_ITERATIONS`, and `GC_WATCHDOG_THREADS` to scale it for CI or local stress. +- `scripts/gc-correctness-stress.sh` runs the randomized graph verification case across default, Phase-2A, descriptor-split, forced-serial, mark-concurrent, region-evacuate, and phase3-observe policy sets. It mutates cross-thread graph edges, weak pairs, guardian-registered objects, locked objects, and bytevector payloads while opportunistically forcing collections. Set `GC_STRESS_ITERATIONS`, `GC_STRESS_THREADS`, `GC_STRESS_NODES`, `GC_STRESS_CASES`, and `GC_STRESS_TIMEOUT_SECONDS` to scale it. +- `scripts/gc-special-stress.sh` runs focused special-reachability cases across default, Phase-2A, descriptor-split, forced-serial, mark-concurrent, region-evacuate, and phase3-observe policy sets. It verifies weak pairs, ephemerons, guardians, locked objects, combined special objects, and cross-thread combined special objects. With `GC_SPECIAL_REQUIRE_TELEMETRY=1`, it also runs a compact telemetry baseline for `special-reachability` under `mark-concurrent` and `phase3-observe`, then fails unless STW special, guardian/finalizer, weak-pair, ephemeron, remark-drain, and mark-state evidence are nonzero. Set `GC_SPECIAL_ITERATIONS`, `GC_SPECIAL_TELEMETRY_ITERATIONS`, `GC_SPECIAL_THREADS`, `GC_SPECIAL_ITEMS`, `GC_SPECIAL_POLICIES`, `GC_SPECIAL_MODES`, and `GC_SPECIAL_TIMEOUT_SECONDS` to scale it. +- `scripts/gc-shadow-reachability.sh` runs a bounded shadow reachability oracle across default, Phase-2A, descriptor-split, and forced-serial policy sets. It independently computes expected liveness for generated object graphs, drops unreachable strong references, forces repeated collections, and verifies that reachable weak keys stay valid while unreachable weak keys clear. Set `GC_SHADOW_ROUNDS`, `GC_SHADOW_NODES`, `GC_SHADOW_ROOTS`, `GC_SHADOW_POLICIES`, and `GC_SHADOW_TIMEOUT_SECONDS` to scale it. +- `scripts/gc-region-stress.sh` runs mutation-heavy workloads with `region-observe`, `region-select`, and `region-evacuate`, then fails on missing or inconsistent region relocation evidence. Optional strict gates also require fallback suppression plus selected-set shrink proof, forwarding-marker, target-link, live-slot rewrite, production-order live-slot commit, source-retirement readiness, reversible source-retire empty/restore, one-way live-slot rewrite, target-retention, cumulative target-retain, target-release suppression, target-live-through-emit, slot-live-through-emit, source-live-through-emit, source-sweep accounting, production-commit blocker evidence, and with `GC_REGION_REQUIRE_SPECIAL_SELECTOR=1`, special-reachability selector blocker evidence. It prints each proof block so the next partial-evacuation slice has concrete reference-update, selected-source accounting, and destination-space budgets. `make gc-region-stress` is the focused Phase-3A guard. +- `scripts/gc-mark-barrier-stress.sh` runs mutation-heavy workloads with `mark-satb`, `mark-queue`, `mark-concurrent`, `mark-apply`, and `region-evacuate`, then fails if the SATB pre-write observer does not record pre-write traffic, overwritten heap references, nonstatic queue candidates, per-thread buffer traffic, bounded queue enqueue/drain evidence, concurrent-observer drain evidence, STW remark drain evidence, segment-indexed mark-bitmap evidence, bitmap-to-collector old-space preflight evidence, or owner-local mark-apply evidence. With `GC_MARK_REQUIRE_WORKER=1`, it also requires a clean observer-worker start, asynchronous worker wake/drain evidence, zero synchronous fallback drains, GC suspend/resume handoff evidence, and worker-owned bitmap scan events/bits; the wrapper enables `CHEZ_GC_MARK_WORKER_BITMAP_SCAN=1` automatically for those worker-required runs unless `GC_MARK_REQUIRE_WORKER_BITMAP_SCAN` overrides it. With `GC_MARK_REQUIRE_WORKER_HANDOFF=1`, it also enables `CHEZ_GC_MARK_WORKER_HANDOFF_PROOF=1` and requires nonzero ready handoff evidence, cumulative worker scan evidence, matching STW mark-apply accounting, representative copy-reconciliation evidence when copy-mode entries exist, and an explicit generated-traversal-missing blocker. With `GC_MARK_REQUIRE_SPECIAL=1`, it adds `special-reachability` to the default mark workload list and requires nonzero concurrent-mark special, guardian/finalizer, weak-pair, and ephemeron subphase evidence in the same focused mark gate. It also fails on queue/buffer/bitmap overflow, real-apply failures, and dirty-card records without generated-store pre-write records. It sets `GC_BASELINE_IN_PLACE_MIN_GENERATION=1` by default so `mark-apply` rows exercise mark-mode segments; override with `GC_MARK_IN_PLACE_MIN_GENERATION`. Including `region-evacuate` keeps the focused Phase-3B/3C decision gate paired with Phase-3A preflight/update-edge evidence. `make gc-mark-barrier-stress` is the focused Phase-3B/3C barrier and handoff guard. +- `scripts/gc-satb-barrier-audit.sh` statically checks the generated SATB pre-write C-entry wiring, `S_dirty_set` old-value-before-assignment ordering, generated `build-dirty-store` wrapping, set/CAS primitive routing for pairs, boxes, vectors, and records, runtime/interpreter field-mutator wrappers through `DIRTYSET`, code-object write bracketing and mutator FFI registration, and the FFI inventory's blocking/unknown review gate. It is not proof that a production concurrent marker is safe, but it prevents the known generated/runtime/interpreter/code/FFI mutation paths from silently escaping the barrier evidence gate. +- `scripts/gc-satb-barrier-audit.sh` statically audits the SATB barrier wiring. It fails if generated `build-dirty-store` stops routing assignments through the pre-write slot, if the C entry/boot-equate/install wiring regresses, if runtime/interpreter setters stop routing through `S_dirty_set`, if runtime `S_dirty_set` no longer snapshots the overwritten value before assignment, if code-object mutators stop bracketing writes with code-write hooks, if FFI audit reports blocking non-collect-safe or unknown entries, or if dirty-card telemetry is disconnected. `make gc-satb-barrier-audit` is the cheap preflight before dynamic mark stress. +- `scripts/gc-sanitizer-smoke.sh` builds side-by-side sanitizer Chez runtimes with `ENABLE_GC_TELEMETRY` and runs reduced shadow, special, and randomized correctness stress. It supports `GC_SANITIZERS='address undefined thread'`; unsupported local sanitizer modes are recorded as `skip`, while build or runtime sanitizer findings fail the run. +- `scripts/gc-qualification.sh` runs the GC evidence suite into one output directory and records per-step logs in `summary.csv`. `GC_QUALIFICATION_PROFILE=smoke` keeps dimensions short enough for local iteration by running the `default`, `phase2a`, `descriptor-split`, `phase2a-split`, `region-observe`, `region-select`, `region-evacuate`, `mark-satb`, `mark-queue`, `mark-concurrent`, and `phase3-observe` policy presets, including the `one-allocator-idle` workload, and preserving a five-repetition throughput comparison for the single-thread gate. It also runs `gc-satb-barrier-audit`, `gc-region-stress`, and `gc-mark-barrier-stress`. Set `GC_QUALIFICATION_STRICT_PHASE3=1` to make the qualification wrapper require observer-worker lifecycle, worker-handoff proof, and same-gate special-reachability evidence from `gc-mark-barrier-stress`, special-reachability telemetry evidence from `gc-special-stress`, the full current region relocation proof chain from `gc-region-stress` (update-edge, forwarding-marker, target-link, live-slot, production-order live-slot commit, source-retire, source-retire empty/restore, one-way rewrite, target-retention, cumulative target-retain, target-release suppression, target-live-through-emit, slot-live-through-emit, source-live-through-emit, source-sweep accounting, and production-commit blocker evidence), and reserve-fallback suppression plus selected-set shrink evidence from an extra `gc-region-stress` pass with `CHEZ_GC_REGION_RESERVE_LIMIT_BYTES=0`; override those independently with `GC_QUALIFICATION_MARK_REQUIRE_WORKER`, `GC_QUALIFICATION_MARK_REQUIRE_WORKER_HANDOFF`, `GC_QUALIFICATION_MARK_REQUIRE_SPECIAL`, `GC_QUALIFICATION_SPECIAL_REQUIRE_TELEMETRY`, `GC_QUALIFICATION_REGION_REQUIRE_FALLBACK`, or the corresponding `GC_QUALIFICATION_REGION_REQUIRE_*` proof gate variables. The fallback qualification dimensions are independently tunable with `GC_QUALIFICATION_REGION_FALLBACK_ITERATIONS`, `GC_QUALIFICATION_REGION_FALLBACK_THREADS`, `GC_QUALIFICATION_REGION_FALLBACK_WORKLOADS`, `GC_QUALIFICATION_REGION_FALLBACK_POLICY_PRESETS`, `GC_QUALIFICATION_REGION_FALLBACK_COLLECT_TRIP_BYTES`, `GC_QUALIFICATION_REGION_FALLBACK_THROUGHPUT_ITERATIONS`, and `GC_QUALIFICATION_REGION_FALLBACK_THROUGHPUT_REPETITIONS`. `GC_QUALIFICATION_PROFILE=full` leaves the baseline, throughput, stress, and mats defaults intact for release evidence. Set `GC_QUALIFICATION_INCLUDE_SANITIZERS=1` to include sanitizer builds. +- `scripts/gc-mats.sh` runs Chez mats targets against side-by-side GC builds. The default GC gate runs `thread.mo` and `foreign.mo` against `build/chez-gc-default` and `build/chez-gc-telemetry`; telemetry builds run both `CHEZ_GC_TELEMETRY=0` and `CHEZ_GC_TELEMETRY=1` with sidecar JSONL capture so mats stderr assertions remain valid. Set `GC_MATS_TARGETS='test-one test-one-safe'` or another mats target list for broader suites. +- `CHEZ_GC_PARALLEL_SWEEPERS=N` caps auxiliary parallel-GC sweepers at runtime. `CHEZ_GC_PARALLEL_SWEEPERS=auto` uses online CPU count minus one, clamped to Chez's hard cap. Unset/default preserves existing Chez behavior. Telemetry records `sweeper_cap`, `waiting_threads`, and `aux_sweepers`, so Phase-2A runs can compare worker counts without rebuilding. +- `CHEZ_GC_SWEEPER_MIN_WORK_BYTES=N|auto` enables the useful-work side of the Phase-2A worker-count policy. After setup, greedy assignment, and dirty-segment assignment have made the work estimate meaningful, the collector trims scheduled auxiliary sweepers when the estimated bytes of GC work are too small to justify the requested worker count. `auto` currently means four Chez segments of estimated work per active participant. Unset/default preserves existing behavior. Telemetry records `sweeper_estimated_work_bytes`, `sweeper_min_work_bytes`, `requested_aux_sweepers`, and `effective_aux_sweepers`. +- `CHEZ_GC_SWEEPER_ASSIGN=greedy` enables an opt-in Phase-2A assignment experiment. It preserves segment ownership and the existing remote-rescan protocol, but after dirty-segment setup it reassigns already-started sweepers by a conservative work estimate: target-generation sweep ranges, sweep-next segment lists, and dirty-segment lists. Unset/default preserves Chez's previous assignment order. Telemetry records `greedy_assignment`, so raw JSONL logs are self-describing even outside the baseline matrix. +- `CHEZ_GC_DIRTY_ASSIGN=greedy` enables the Phase-2A.4 independent dirty-segment partitioning experiment. After normal dirty setup and sweeper assignment, it detaches movable whole dirty segments and redistributes them to the least-loaded sweeper representative by dirty-card work estimate. Segments whose creator still has an active thread stay on their original `thread_gc`, because `sweep_dirty_segments` uses the executing `thread_gc`'s allocation bounds. The experiment does not split a segment, change `seginfo->creator`, or alter forwarding/mark ownership. Telemetry records `dirty_greedy_assignment`, `dirty_rebalanced_segments`, and `dirty_rebalanced_bytes`. +- `CHEZ_GC_FORCE_SERIAL=1` provides the implementation-guardrail fallback for bisecting collector corruption. It forces ordinary collections through `gc-ocd` with telemetry reason `forced-serial`, while leaving object-count, backreference, count-root, and static-generation dispatch on `gc-oce`. + +Phase-1 rendezvous and FFI hardening has also started: + +- Blocking socket calls in `std/net/io`, `std/net/repl`, `std/repl/server`, `std/net/grpc`, `std/net/s3`, and `std/net/socks` are now collect-safe where their arguments are pointer/bytevector buffers rather than strings. +- `std/repl/server` now passes null native address buffers to collect-safe `accept`; it does not hand movable Scheme bytevectors to a collect-safe syscall. +- `sigwait`, `kevent`, `signalfd` read, and `posix`/security `waitpid` calls that use native `foreign-alloc` buffers are now collect-safe. +- `std/security/sandbox` and `std/security/privsep` pipe read/write paths now copy through native buffers before invoking collect-safe blocking `read`/`write`, avoiding blocked syscalls holding movable Scheme bytevector addresses. +- `std/os/iouring`, `std/os/epoll-native`, `std/os/inotify-native`, `std/os/aproc`, `std/pcap`, and `std/net/tls-rustls` now avoid passing movable Scheme bytevectors to collect-safe blocking waits/connects/reads. They either use native pointer signatures directly or copy through `foreign-alloc` buffers around the collect-safe call. +- `CHEZ_GC_SAFE_USER_MUTEX=1` enables the Phase-1 user-level blocking mutex experiment. `S_mutex_acquire` deactivates the current Scheme thread only while blocking on non-runtime mutexes; `S_tc_mutex` and `S_alloc_mutex` remain on the original active-thread path to preserve collector lock ordering. Unset/default preserves upstream behavior. +- `make gc-ffi-audit` currently reports `blocking_without_collect_safe=0` and `needs_review=0`. The audit classifies concrete FFI entries as blocking, bounded setup, native handle/pointer calls, Chez internals, macro/dynamic FFI generators, or explicitly reviewed non-collect-safe setup/integrity calls. The reviewed non-collect-safe categories are intentionally not blanket-annotated because their current APIs accept movable Scheme bytevectors or strings; they need native-buffer wrappers before any future collect-safe conversion. +- `tests/test-repl-server.ss` no longer uses `nc -q`, which is unsupported by Apple `nc` and previously caused blank client responses on macOS. + +Phase-2A low-risk scheduler work has started: + +- `CHEZ_GC_PARALLEL_SWEEPERS=auto` provides the CPU-aware cap experiment described in 2A.1 without changing the default. +- `CHEZ_GC_SWEEPER_MIN_WORK_BYTES=N|auto` completes the useful-work-estimate part of 2A.1 as an opt-in policy. It never creates more workers than the CPU/configured cap; it can only reduce the scheduled worker count for a given collection. +- `CHEZ_GC_SWEEPER_ASSIGN=greedy` provides a conservative implementation of 2A.2. It deliberately does not split any source segment, change `seginfo->creator`, or let two workers write the same forwarding/mark state. +- Remote handoff measurement for 2A.3 is in place, and the `cross-owner-graph` baseline workload exercises it directly. `CHEZ_GC_REMOTE_BATCH=consecutive` enables an opt-in experiment that collapses consecutive top-of-stack remote sends to the same destination while preserving the existing remote-stack delivery order. Unset/default keeps the old one-object send loop. Telemetry records `remote_consecutive_batch`; compare `remote_delivery_batches_total` and `max_remote_delivery_batch` to the send/receive totals before treating the experiment as useful. +- `CHEZ_GC_DIRTY_ASSIGN=greedy` provides a conservative implementation of the dirty-segment slice of 2A.4. It only redistributes independent whole dirty segments whose creator is not an active allocator thread; weak/ephemeron/guardian fixed points remain serialized as required by the plan. +- `CHEZ_GC_FORCE_SERIAL=1` preserves a runtime serial fallback for every experimental scheduler mode. +- The baseline harness can now run `GC_BASELINE_COLLECT_TRIP_BYTES='default 32768 8388608' GC_BASELINE_SWEEPER_CAPS='default auto 1 2 4' GC_BASELINE_SWEEPER_MIN_WORKS='default auto' GC_BASELINE_ASSIGN_POLICIES='default greedy' GC_BASELINE_DIRTY_ASSIGNMENTS='default greedy' GC_BASELINE_REMOTE_BATCHES='default consecutive' GC_BASELINE_DESCRIPTOR_EXPERIMENTS='default observe split' GC_BASELINE_REGION_EXPERIMENTS='default observe select evacuate' GC_BASELINE_MARK_EXPERIMENTS='default satb-observe queue concurrent-observe'` and summarize the collect-trip setting plus all eight policy dimensions in one CSV. For local iteration, use `GC_BASELINE_POLICY_PRESETS='default phase2a descriptor-split phase2a-split region-observe region-select region-evacuate mark-satb mark-queue mark-concurrent phase3-observe'` to force the all-default row, the combined Phase-2A candidate row, the Phase-2B split row, the combined scheduler-plus-split row, Phase-3A observer/selector/preflight rows, the Phase-3B/3C SATB observer/queue/remark handoff rows, and a combined Phase-3 debug-stack row. The summary reports requested and effective auxiliary sweeper counts for the useful-work policy. + +Current verification on macOS arm64: + +- `make build` +- `make test-ffi` +- `make test-security` +- `make test-repl` +- direct `tests/test-grpc.ss` +- direct sandbox fail-closed/entry-point tests: `test-sandbox-script-failclosed.ss`, `test-sandbox.ss`, `test-sandbox-thread-failclosed.ss`, and `test-sandbox-native-timeout.ss` +- telemetry Chez build and smoke test with `CHEZ_GC_TELEMETRY=1` +- baseline smoke plus `make gc-baseline-summary` and `make gc-baseline-compare` +- focused `blocking-io` workload smoke with resource metrics in the summary +- focused `gc-rendezvous-watchdog` smoke for collect-safe blocking I/O, condition wait, and the opt-in user mutex path +- focused `gc-correctness-stress` smoke across default, Phase-2A, descriptor-split, and forced-serial policies +- focused `gc-special-stress` smoke across default, Phase-2A, descriptor-split, forced-serial, mark-concurrent, region-evacuate, and phase3-observe policies +- focused `gc-shadow-reachability` smoke across default, Phase-2A, descriptor-split, and forced-serial policies +- focused `gc-region-stress` smoke across default, region-observe, region-select, and region-evacuate policies +- focused `gc-mark-barrier-stress` smoke across default, mark-satb, mark-queue, mark-concurrent, and region-evacuate policies +- focused `long-lived-dirty` and `special-reachability` baseline smoke with subphase p99 columns in the summary +- focused collect-trip baseline and throughput smoke with `default` and `32768` trip thresholds +- `make gc-mats` default GC-relevant mats pass on default and telemetry builds, including telemetry-on sidecar capture +- throughput smoke with `make gc-throughput` +- decision-gate smoke with `make gc-decision-gate`; local tiny runs are allowed to emit `fail` or `defer` rows in `decision-gate.csv` because performance noise can dominate short samples. Use `GC_DECISION_FAIL_ON_FAIL=1` only for stable target-hardware evidence. +- `make -C build/chez-gc-telemetry` +- `make -C build/chez-gc-default` + +Build a telemetry runtime with a fresh Chez configure, for example: + +```sh +rm -r build/chez .chez +CHEZ_CONFIGURE_EXTRA='CFLAGS+=-DENABLE_GC_TELEMETRY' make build +make gc-ffi-audit +GC_BASELINE_COLLECT_TRIP_BYTES='default 32768 8388608' GC_BASELINE_SWEEPER_CAPS='default auto 1 2 4' GC_BASELINE_SWEEPER_MIN_WORKS='default auto' GC_BASELINE_ASSIGN_POLICIES='default greedy' GC_BASELINE_DIRTY_ASSIGNMENTS='default greedy' GC_BASELINE_REMOTE_BATCHES='default consecutive' JERBOA=./jerboa-bin make gc-baseline +make gc-baseline-summary +make gc-baseline-compare +make gc-pause-attribution +GC_THROUGHPUT_COLLECT_TRIP_BYTES='default 32768 8388608' GC_THROUGHPUT_SWEEPER_CAPS='default auto 1 2 4' GC_THROUGHPUT_SWEEPER_MIN_WORKS='default auto' GC_THROUGHPUT_ASSIGN_POLICIES='default greedy' GC_THROUGHPUT_DIRTY_ASSIGNMENTS='default greedy' GC_THROUGHPUT_REMOTE_BATCHES='default consecutive' JERBOA=./jerboa-bin make gc-throughput +make gc-decision-gate +GC_BASELINE_POLICY_PRESETS='default phase2a' GC_BASELINE_WORKLOADS='short-lived one-allocator-idle' JERBOA=./jerboa-bin make gc-baseline +GC_THROUGHPUT_POLICY_PRESETS='default phase2a' JERBOA=./jerboa-bin make gc-throughput +JERBOA=./jerboa-bin make gc-shadow-reachability +JERBOA=./jerboa-bin make gc-special-stress +JERBOA=./jerboa-bin make gc-correctness-stress +JERBOA=./jerboa-bin GC_QUALIFICATION_PROFILE=smoke make gc-qualification +``` + +When using an alternate Chez prefix for a side-by-side telemetry runtime, pass an absolute `CHEZ_PREFIX`, for example `CHEZ_PREFIX=$PWD/.chez-gc-telemetry`. + +For non-default baseline directories, set `GC_BASELINE_DIR`, for example: + +```sh +GC_BASELINE_DIR=build/gc-baseline-phase2a make gc-baseline +GC_BASELINE_DIR=build/gc-baseline-phase2a make gc-baseline-summary +GC_BASELINE_DIR=build/gc-baseline-phase2a make gc-baseline-compare +GC_BASELINE_DIR=build/gc-baseline-phase2a make gc-pause-attribution +GC_THROUGHPUT_DIR=build/gc-throughput-phase2a make gc-throughput +``` + +If `CHEZ_GC_TELEMETRY=1` produces no `{"event":"chez_gc",...}` lines, the runtime was not rebuilt with `ENABLE_GC_TELEMETRY`. + +If a test checks stderr exactly, set `CHEZ_GC_TELEMETRY_FILE` to a writable JSONL path instead of letting telemetry write to stderr. `make gc-mats` uses this mode for telemetry-on mats runs. + +Recommended sequence: + +1. Measure STW rendezvous and every current GC phase; audit blocking FFI. +2. Improve and validate the existing parallel-STW collector, including a controlled experiment that splits a single mutator's work. +3. If old-generation discovery dominates the pause budget, add concurrent marking plus STW parallel evacuation. +4. Treat concurrent relocation (Shenandoah/ZGC style) as a separate runtime project with a multi-year cost, not an incremental follow-up. + +This is an implementation handoff, not a promise that each option will improve every workload. The go/no-go gates below are intentional. + +## Goals And Non-Goals + +### Target outcomes + +- Reduce p99 and p99.9 allocation stalls caused by GC on 4-16 core hosts. +- Preserve Chez object identity, weak pairs, ephemerons, guardians, locked objects, FFI safety, and supported 32-bit and 64-bit targets. +- Improve throughput for allocation-heavy threaded programs without degrading the single-threaded fast path by more than 3%. +- Report the time to rendezvous separately from the time spent collecting. + +### Explicit non-goals for the first two phases + +- No mutator runs concurrently with copying, forwarding-pointer installation, or mark-bit reuse. +- No change to raw pointer representation, object headers, or user-visible FFI contracts. +- No unbounded thread count. GC worker counts must be capped and CPU-aware. +- No claim of a hard real-time pause bound. + +## What The Vendored Runtime Does Today + +### Collector model + +Chez is already a hybrid, generational collector: + +| Area | Current behavior | Evidence | +| --- | --- | --- | +| Young and ordinary generations | Copies reachable objects from selected source generations into fresh target-generation segments. Forwarded objects hold forward_marker plus a new address. | vendor/ChezScheme/c/gc.c header and copy/sweep_generation_pass | +| Non-moving cases | Marks and sweeps a segment in place when it contains an immobile/locked object, or when a sufficiently compact target-generation segment is retained. | gc.c use_marks, marked_mask, and must_mark handling | +| Old/static objects | The static generation does not move. Code relocations may be discarded when code becomes static. | gc.c header | +| Remembered set | Dirty cards/dirty segment lists track references from older areas into collected generations. | gc.c setup_sweep_dirty/sweep_dirty_segments | +| Special reachability | Weak pairs, ephemerons, guardians, finalization, and locked objects have dedicated fixed-point and post-sweep work. | gc.c check_pending_ephemerons and guardian handling | +| Object scanning | Shape-specific scanning is generated from mkgc.ss into gc-oc*.inc and gc-par.inc. | gc.c header | + +The default build constants set the maximum non-static generation to 4 and the collection trip threshold to 2^(20 + log2(ptr-bytes)), which is 8 MiB on a 64-bit build. Those defaults are defined in vendor/ChezScheme/s/cmacros.ss. They are policy knobs, not a substitute for fixing pause tails. + +### How a collection stops the world + +Allocation marks a collection request pending; it does not collect under the allocator lock. The generated/runtime trap path sends active Scheme threads to collect-rendezvous. The last active thread runs the collection while the other threads are deactivated and wait on collect conditions. + +Relevant control flow: + +1. alloc.c:maybe_queue_fire_collector notices that gen-0 allocation has crossed collect_trip_bytes. +2. schsig.c:S_fire_collector marks every Scheme thread as having pending work. +3. s/7.ss:collect-rendezvous waits until active-threads is one, then calls collect. +4. s/7.ss:docollect asserts that only one thread is active and calls gcwrapper.c:S_do_gc. +5. gcwrapper.c:S_gc chooses the implementation and gc.c performs the collection. + +This is a real STW collector. Parallel workers run only after the mutators have reached the rendezvous; they do not make marking or relocation concurrent with the program. + +### Existing parallel collection + +vendor/ChezScheme/c/gc-par.c compiles a second inclusion of gc.c with ENABLE_PARALLEL. gcwrapper.c:S_gc selects it for threaded collections when there are waiting collector threads, or when there are multiple Scheme threads and at least one preserves allocation ownership. Object counting, backreferences, count roots, and static-generation collections select the non-parallel S_gc_oce path instead. + +The parallel path is substantial, not a stub: + +- setup_sweepers maps thread GC contexts to a persistent worker pool. The current boot configuration permits up to 16 auxiliary sweepers plus the collecting thread; see maximum_parallel_collect_threads in vendor/ChezScheme/boot/pb/equates.h. +- run_sweepers runs dirty-card processing and sweep_generation_pass on each assigned context, then waits until no remote work remains. +- A sweeper owns a segment through seginfo->creator. A reference into a remote, not-yet-marked segment queues the referring object for the owning sweeper. This preserves single-writer forwarding and mark state. An object can be rescanned several times as remote references are discovered. +- sweep_mutex protects worker state and remote-work handoff. The code explicitly forbids taking the TC mutex during sweeping; the allocation mutex is the only permitted runtime lock while allocating GC-owned temporary segments. + +The important limitation is task granularity: work is assigned by thread_gc context, which reflects allocation ownership. A program with one allocator, one large heap, and many idle threads can have little useful GC parallelism. Cross-owner references also add rescan and mutex costs. Root setup, selection of old segments, thread-list traversal, symbol handling, several fixed points, and finalization remain serial around the worker phase. + +### Thread and FFI facts that constrain the plan + +- collect-safe foreign calls deactivate the caller while native code runs, allowing the rendezvous to proceed. The current TCP accept, connect, read, and write declarations in lib/std/net/tcp.ss already use it. +- A collect-safe call may allow objects to move. Native code must not retain a raw address into a movable Scheme object, and large bytevector writes must be chunked or copied to native-owned storage. lib/std/web/rack.ss documents the bounded-copy pattern. +- thread.c:S_condition_wait deactivates a Scheme thread before blocking and reactivates it after wakeup. It is therefore not evidence that the runtime lacks a GC-safe condition wait. +- thread.c:S_mutex_acquire calls the OS mutex lock while the Scheme thread remains active. A contended user mutex can extend the time to rendezvous. Do not blindly change this common C routine: it also backs the TC and allocation locks, whose ordering and semantics are collector invariants. + +The existing docs/chez-gc-findings.md is useful historical workload evidence, but it contains an explicitly unverified internal-sequencing hypothesis and a TCP comment that no longer matches the current FFI declarations. Treat the vendored source and a fresh reproduction as authoritative before acting on it. + +## Phase 0: Establish A Trustworthy Baseline + +Size: 2 engineers, 3-5 weeks. +Risk: low. +Expected pause improvement: none by itself; it prevents investing in the wrong problem. + +### Instrumentation + +Add a compile-time-disabled-by-default GC event stream. Do not allocate Scheme objects or take the TC mutex while recording it. A fixed-size per-worker native ring buffer, drained after collection or through a debug API, is sufficient. + +Record for each collection: + +- generation range, copy-versus-mark segment counts, bytes before/after, and total live/copied/marked/swept bytes; +- request-to-last-thread-deactivated time, total STW time, and each serial phase duration; +- worker count, per-worker useful work, idle time, remote sends/receives, remote rescan count, remote-queue lock time, and maximum queue depth; +- dirty-card/dirty-segment scan time; ephemeron, guardian, weak-pair, and finalizer times; and time reopening allocation areas; +- the dispatch choice: gc-011, gc-ocd, gc-oce, or gc-par, and why. + +Instrument these seams first: + +- vendor/ChezScheme/c/schsig.c:S_fire_collector +- vendor/ChezScheme/s/7.ss:collect-rendezvous and docollect +- vendor/ChezScheme/c/gcwrapper.c:S_gc +- vendor/ChezScheme/c/gc.c:GCENTRY, run_sweepers, sweep_generation_pass, send_and_receive_remote_sweeps, and post-sweep special-reachability work. + +Use monotonic wall time for pause latency and process CPU time for worker cost. Existing REPORT_TIME counters in gc.c are a useful starting point, but the new data must be machine-readable and available in normal debug builds. + +### Workload suite + +Build a small C/Scheme-independent benchmark driver, then add Jerboa-facing tests only after the runtime measurements are sound. Run each workload at 1, 2, 4, 8, and 16 active mutators with a fixed collection trip threshold and an adequately provisioned collection trip threshold. The current baseline exposes that dimension through `GC_BASELINE_COLLECT_TRIP_BYTES`. + +| Workload | What it identifies | +| --- | --- | +| Short-lived pair/vector churn | Nursery throughput and gen-0 pause distribution | +| Long-lived mutable graph with old-to-young stores | Dirty-card and remembered-set cost | +| One allocator plus idle threads | Current ownership-induced load imbalance | +| Many allocators with cross-thread graph references | Remote rescan and queue contention | +| Locked/immobile objects plus large code/data | Mark-in-place and fragmentation behavior | +| Weak pairs, ephemerons, guardians, and finalizers | Fixed-point correctness and tail time | +| Collect-safe blocking I/O plus allocators | Rendezvous safety and FFI address lifetime | +| Contended user mutex plus allocators | Active-thread rendezvous tail | + +For every run retain raw pause samples and report p50/p95/p99/p99.9, maximum, allocation throughput, CPU utilization, resident memory, and correctness failures. Do not decide from an average pause. + +### Phase-0 exit gate + +Proceed to parallel work only if at least 30% of p99 pause time is inside gc.c after the last mutator deactivates. If rendezvous time dominates, finish the FFI/blocking audit first. If serial special-reachability phases dominate, target those phases before changing the worker scheduler. + +## Phase 1: Rendezvous And FFI Hardening + +Size: 1-2 engineers, 4-7 weeks. +Risk: medium because FFI address lifetime is correctness-critical. +Expected benefit: removes pathological and unbounded stop-the-world entry latency; does not reduce the cost of a healthy GC cycle. + +### Plan + +1. Inventory all foreign-procedure, foreign-callable, direct C blocking paths, and native callbacks. Classify each as bounded/nonblocking, potentially blocking, or retaining a Scheme address. +2. Require a documented reason for every potentially blocking call that is not collect-safe. Add a test that triggers GC while it is blocked. +3. For collect-safe calls, prove that every pointer argument remains valid: bounded native copies, stable external allocations, ftype/reference support where appropriate, or a pin/lock protocol with a bounded lifetime. +4. Add a separate design for user-level blocking mutex acquisition. Prefer a condition-wait-based or new primitive path that deactivates only while blocked. Leave S_mutex_acquire, S_tc_mutex, and S_alloc_mutex intact until a lock-order review and stress test prove the split is sound. +5. Add watchdog-based tests that fail rather than hang when a collection cannot rendezvous within a generous test deadline. + +### Pros and cons + +Pros: + +- Directly addresses the worst observed symptom: a collection that cannot start because a thread is in the wrong native/blocking state. +- Keeps object layout and the collector algorithm unchanged. +- Makes future concurrent GC safer because its FFI contract is stricter too. + +Cons: + +- It does not make a completed STW GC shorter. +- Collect-safe transitions cost something and expose moving-address bugs in incorrect native bindings. +- Making arbitrary mutex acquisition GC-safe requires a new, carefully scoped implementation; it is not a one-line flag change. + +## Phase 2: Improve The Existing Parallel-STW Collector + +Recommendation: implement this after Phase 0. +Size: 2-3 engineers, 4-7 months for a production implementation; a 6-10 week experiment can validate the core premise. +Risk: medium-high. +Expected benefit: 1.5-4x lower collector work time on balanced, multi-core, allocation-heavy heaps. It does not improve time-to-safepoint and will not provide sub-millisecond pauses on large live heaps. + +### 2A. Low-risk scheduler and policy improvements + +Implement these first, one feature per benchmarked change: + +1. Add a GC worker-count policy: min(online_cpus - active_runtime_need, configured_cap, useful_work_estimate). Keep the existing cap as a hard safety limit; do not create workers per collection. +2. Score each thread_gc before assignment by dirty-card bytes, source segment bytes, estimated live bytes from the prior cycle, and pending special work. Use a stable greedy/bin-packing assignment instead of only round-robin assignment of remaining contexts. +3. Batch remote handoffs. Replace single-object handoff under sweep_mutex with bounded per-destination batches while retaining the current ownership protocol and a clear completion detector. +4. Parallelize only independent post-root tasks after measuring them, starting with dirty-segment list partitions. Keep weak/ephemeron/guardian fixed points serialized until a specific proof and test demonstrate independence. +5. Add a serial fallback for one worker, object counting, backreferences, debug heap checking, and every failure to start an auxiliary worker. + +Do not change the SEGMENT_IS_LOCAL, forwarding, or mark-bit ownership rule in this subphase. Its purpose is to quantify the value available without a new object-work model. + +### 2B. Experiment: split one mutator's heap into GC work contexts + +This is the key go/no-go experiment. Current parallelism is constrained by allocation ownership, so a single allocating thread can leave most workers idle. Build an internal, debug-only experiment that lets multiple workers process disjoint, closed source-segment ranges from one original owner. + +Suggested design: + +1. At STW start, close all allocation areas as the current collector already does. +2. Build immutable GC work descriptors for a source segment or a bounded homogeneous-space range. A descriptor records source/target generation, space, traversal bounds, original owner, and a logical GC-owner context. +3. Give each worker private destination allocation state, sweep stack, remote-send buffer, and bitmap-overhead accounting. Never let two workers install forwarding information or mark bits in the same source segment. +4. Publish descriptors through a work-stealing deque only when their source range is disjoint and owner metadata is visible with release/acquire ordering. Cross-range pointers use the existing remote-rescan protocol initially. +5. Keep roots and object metadata whose shape requires remote inspection on the original owner until the experiment has an explicit safe protocol. +6. Compare p99 and CPU cost against the unmodified ownership scheduler on the one-allocator workload. Tear out the experiment if it does not reduce collector time by at least 30% at four cores. + +This needs logical GC-owner state rather than simply changing seginfo->creator: that field is tied to a real thread_gc allocation context, and changing it casually can break allocation state, dirty processing, and remote-reference routing. + +### 2C. Productionize only after the experiment wins + +If 2B wins, replace the ad hoc descriptor logic with a small GC task runtime: + +- bounded per-worker deques plus work stealing; +- a task state machine: new, owned, published, completed, with assertions on every transition; +- separate queues for scan work and remote rescan work, so remote churn cannot starve local scanning; +- a single termination protocol that observes both queues, all active workers, and ephemeron-triggered work; +- deterministic stress mode that injects yields at task transitions; +- debug verification that every old object was copied exactly once or marked exactly once, and that every relocated pointer is valid. + +### Benefits and costs + +| Benefits | Costs and limits | +| --- | --- | +| Uses existing copying/marking semantics and generated object scanner. | All mutators remain stopped; rendezvous and serial root phases still bound latency. | +| Can reduce pause duration without a load barrier on every pointer read. | More synchronization can erase gains on pointer-rich, cross-owner graphs. | +| Keeps current FFI and tagged-pointer representation. | Splitting ownership needs new GC-local allocation state and extensive race testing. | +| Allows a high-quality serial fallback. | More workers can reduce application throughput through CPU and memory-bandwidth contention. | + +## Phase 3: G1-Style Region Selection And Concurrent Marking + +This phase has two separable pieces. Do not combine them in the first patch. + +### 3A. Region/collection-set policy + +Size: 3-4 engineers, 9-15 months. +Risk: high. +Expected benefit: bounds individual old-generation evacuation pauses by selecting work to fit a budget, rather than collecting whole generations. + +G1 is the closest conceptual model. It partitions the heap into regions, marks globally, chooses regions likely to yield space, and evacuates selected regions in parallel. It predicts how much work fits a pause target. G1 still has STW evacuation; it is not a real-time collector. [Oracle's G1 documentation](https://docs.oracle.com/en/java/javase/26/gctuning/garbage-first-g1-garbage-collector1.html) and its [tuning guide](https://docs.oracle.com/en/java/javase/21/gctuning/garbage-first-garbage-collector-tuning.html) are useful reference designs. + +Chez already has segments and dirty cards, but it does not have G1's region-specific remembered sets. Its dirty tracking is generation-oriented. Collecting only some segments from a generation means unselected peer segments can point to selected ones; their references must be found and updated. A generation-level card table cannot identify that set cheaply. + +Required work: + +1. Define a region identity separate from generation and maintain a precise or conservative per-region remembered set on every pointer store. This branch has debug bounded distinct edge telemetry and now classifies selected-set inbound, internal, outbound, and total update edges during preflight. +2. Record per-region live-byte, scan-cost, and evacuation-cost estimates. This branch has conservative segment-level estimates; production concurrent mark data must replace the whole-segment fallback. +3. Add a collection-set selector with a hard pause-work budget and emergency reserve policy. This branch has a debug selector controlled by `CHEZ_GC_REGION_PAUSE_BUDGET_BYTES` and now records whether each nonzero evacuation requirement is reserve-ok or would need fallback handling. When reserve is insufficient, the telemetry path now suppresses relocation target/copy/forwarding work and falls back to ordinary STW collection for that cycle instead of reporting a half-plan. The remaining production work is to make that fallback part of an exposed collector mode with real allocation retry, evacuation retry, and selected-set shrinking policy rather than only a telemetry-cycle suppression branch. +4. Preserve pinned/locked/immobile segments, large objects, weak references, and code relocation rules. Exclude a region that cannot be evacuated. +5. Add evacuation-failure handling before exposing the feature. The fallback must be correct when there is insufficient destination space; Java G1's evacuation failures are a warning that this is not an optional detail. + +Pros: predictable incremental old-space work, less fragmentation, and a natural input to the Phase-2 task scheduler. + +Cons: write-barrier cost on every store, remembered-set memory overhead, and a very large correctness surface. Without concurrent marking, full liveness discovery still produces a large STW phase, so region selection alone is not a complete latency solution. + +### 3B. Concurrent mark, STW parallel evacuation + +Size: 3-5 engineers, 12-20 months after 3A-level barrier infrastructure. +Risk: very high. +Expected benefit: removes most whole-heap old-generation tracing from the pause; evacuation, root snapshot, and final remark remain STW. + +This is a staged design closer to G1 than to ZGC: + +1. A short STW initial-mark snapshots roots and enables a concurrent marking epoch. +2. Mutators execute a verified snapshot-at-the-beginning (SATB) pre-write barrier or a proven incremental-update barrier on every heap pointer store. +3. Concurrent workers trace only non-moving regions. Mark bitmaps must be epoch-safe and cannot reuse the STW collection's forwarding/mark state. +4. A short STW remark drains buffers, processes weak/ephemeron semantics, and selects regions. +5. Existing/new parallel STW evacuation compacts that bounded collection set. + +The barriers must cover generated Scheme code, interpreter/runtime stores, record/vector/pair mutation, atomic operations, code-object changes, and FFI paths. Missing one barrier is a heap corruption bug, not a degraded optimization. This branch now has debug SATB pre-write coverage for generated `build-dirty-store` paths and runtime `S_dirty_set`, fixed per-thread candidate buffers, a retained candidate queue, default-on threaded observer-worker scaffolding in telemetry builds with start/wake/asynchronous-drain/fallback/suspend/resume lifecycle counters plus opt-in worker-owned bitmap scan counters, explicit STW remark draining, an epoch-tagged segment-indexed bitmap mark-state handoff, bitmap-to-collector old-space preflight, an opt-in owner-local mark-apply prototype across mapped sweeper owner contexts for mark-mode collector state with targeted record/reference-array coverage, an opt-in copy-mode SATB relocation prototype with post-copy forwarding reconciliation, conservative region live/scan/evacuation cost estimates, selected-set remembered-edge update-cost telemetry, selected-set destination-reserve dry-run telemetry, selected-set relocation-plan telemetry, normal-heap target-inventory telemetry, telemetry-event-scoped target reservation leases, leased-target allocator cursor telemetry, source-to-target reservation-map telemetry, selected-object copy-schedule placement telemetry with per-target `space`/`generation` homogeneity, source/destination/size copy-entry metadata telemetry, detached-target physical byte-copy telemetry, bounded logical-and-physical forwarding-table metadata telemetry, reversible pair/symbol source forwarding-marker telemetry, detached-copy slot-rewrite telemetry, reversible target-publication metadata preflight telemetry, reversible occupied-list/accounting target-link telemetry, range-aware forwarding lookup telemetry, conservative remembered-edge rewrite-preflight telemetry, exact selected-object slot-rewrite preflight telemetry for pair-like, symbol, closure, record, fixed-layout port, vector/any-stencil-vector, box/TLC, ratnum/exactnum, reference-array, and scalar pointer-free typed-object layouts plus selected-object deferral reason buckets, reversible live source/external slot rewrite telemetry plus opt-in production-order deferred live-slot rehearsal telemetry, opt-in source-retirement readiness telemetry that cross-checks selected source forwarding coverage against target publication/link, forwarding markers, and commit-ordered rewrite slots, opt-in reversible source-retire empty/restore telemetry that models copied-source chunk unused-list and segment-count accounting, opt-in one-way live-slot rewrite proof telemetry that applies and verifies forwarded source/external slots after the target/source proof window before telemetry restoration, opt-in target-release suppression proof telemetry that carries retained copied targets across the ordinary telemetry-finish release branch before explicit cleanup, opt-in target-live-through-emit proof telemetry that links/accounts retained copied targets for the final emitted heap snapshot before cleanup, opt-in slot-live-through-emit proof telemetry that keeps saved source/external slots forwarded to those live retained targets through the final emitted heap snapshot before restoring slots, opt-in source-live-through-emit proof telemetry that keeps selected source retirement evidence present through the final emitted heap snapshot before restoring source metadata, opt-in source-sweep accounting proof telemetry that verifies selected-source retired accounting in that final snapshot while preserving cleanup and sweep-integration blockers, an opt-in production-commit handoff gate that reports missing prerequisite proofs versus the current wrapper-only durable-production blockers, exact non-selected dirty-card slot-rewrite preflight telemetry for pair-like, symbol, weak-pair, reference-array, port, unmarked impure-record, closure, supported dynamic typed-object cards, ratnum/exactnum, and scalar pointer-free typed-object cards, selector-side spanning/group exclusion, explicit region exclusion for port/code spaces, opt-in closed-port region-selection stress coverage, space-aware object-preflight telemetry, and an expanded static SATB barrier wiring audit for generated, runtime/interpreter, code-object, and FFI paths. The next implementation must turn the worker-owned bitmap scan proof into production marker workers that invoke safe generated mark traversal from outside the GC-begin proof window, broaden exact slot scanning to remaining deferred selected typed-object and external dirty-card layouts, integrate weak/ephemeron/finalizer processing with the remark phase, broaden forwarding-marker support beyond pair/symbol fixed-size starts with per-layout proof, move object copying/forwarding into Chez generated collector ownership, convert source-retirement proof into real selected-source retirement and sweep accounting, install durable production forwarding markers for copied starts, and leave target segments plus forwarded slots live after GC return by removing slot restoration and the explicit cleanup release only after generated-copy ownership is real. The production-commit gate should stay blocking until those integration blockers disappear under stress. + +Pros: the most realistic route to substantially lower old-generation pauses while preserving moving objects and a conventional pointer representation. + +Cons: high steady-state write-barrier and concurrent-CPU cost, more heap headroom is needed to keep up with allocation, and the final remark plus evacuation can still be long. Concurrent collectors can fall back to a degenerate STW cycle when allocation outruns collection; this failure mode must be designed and surfaced clearly. + +## Phase 4: Concurrent Relocation (Shenandoah Or ZGC Class) + +Recommendation: do not schedule this without a dedicated runtime team and a latency requirement that Phase 3 cannot meet. + +### Shenandoah-like design + +Size: 5-8 engineers, 24-36+ months. +Model: concurrent mark and concurrent evacuation using an object indirection/Brooks forwarding word plus load and write barriers. + +[OpenJDK's Shenandoah design](https://openjdk.org/jeps/189) performs marking and compaction concurrently, trading CPU cycles and space for short pauses. It uses an object indirection pointer so mutators can follow an object while the collector moves it. + +For Chez, this means either adding per-object forwarding/indirection storage or building a side metadata table, and inserting a read barrier into every compiled and runtime pointer dereference. The current forwarding representation overwrites object words, which is safe only while all mutators are stopped. + +Required new contracts: + +- a load barrier in all generated machine-code pointer loads, runtime helpers, and FFI transitions; +- SATB/incremental-update write barriers and concurrent mark queues; +- from-space lifetime, forwarding races, and self-healing reference updates; +- an explicit pinning/handle model for native code that keeps addresses; +- concurrent-safe weak/ephemeron/finalizer semantics and a degenerated STW fallback; +- a platform memory-model and sanitizer test matrix. + +Pros: the only option here with a plausible low-millisecond pause profile for large live heaps. + +Cons: permanent read-barrier overhead, larger objects or metadata, broad compiler/FFI changes, difficult debugging, and a major throughput risk. It is not compatible with the existing forwarding scheme as a local patch. + +### ZGC-like colored-pointer design + +Size: 6-10 engineers, 36-48+ months. +Model: concurrent relocation with colored references, load barriers, and generational store barriers. + +[Generational ZGC](https://openjdk.org/jeps/439) separates young and old work while using colored pointers plus load and store barriers; it targets pauses below one millisecond with significant implementation complexity. Its design also benefits from Java's 64-bit address-space and JIT barrier-injection assumptions. + +Chez is a poor direct fit until proven otherwise: it has tagged Scheme values, supports more than one word size and platform model, exposes raw addresses through FFI, and does not currently have a general compiler barrier-insertion framework. A colored-pointer prototype must first prove that tag bits, virtual address layout, foreign pointers, stack/register roots, and serialized object formats can coexist. Failure of any proof ends the approach. + +Pros: best theoretical pause profile and scalable generational policy. + +Cons: near-rewrite of representation, compiler, runtime, and FFI boundary; ongoing barrier cost; non-portable address-space assumptions; and no safe incremental migration from the current collector. + +## Option Comparison + +| Approach | Mutators run during GC work? | Pause effect | Engineering size | Memory/throughput cost | Recommendation | +| --- | --- | --- | --- | --- | --- | +| Baseline plus FFI/rendezvous hardening | No | Removes pathological entry stalls | 3-7 weeks | negligible to small | Do now | +| Tune current parallel STW scheduler | No | Helps balanced threaded heaps | 1-3 months | extra workers and queues | Do now after measurement | +| Dynamic segment tasks/work stealing | No | Best near-term reduction in collector portion of pause | 4-7 months | synchronization and GC-local state | Do after experiment gate | +| Region collection sets only | No | Bounds selected old-space evacuation, not global marking | 9-15 months | remembered sets, write barrier | Defer | +| Concurrent mark plus STW evacuation | Mark only | Removes most old marking from pauses | 12-20 months | barriers, mark queues, heap headroom | Long-term candidate | +| Concurrent sweep of non-moving areas | Sweep only | Can reduce post-mark work, limited compaction benefit | 6-12 months | allocator/free-list concurrency | Consider only after concurrent mark | +| Shenandoah-like concurrent evacuation | Yes | Low pauses if collector keeps up | 24-36+ months | read/write barriers, metadata, headroom | Research only | +| ZGC-like colored pointers | Yes | Lowest theoretical pauses | 36-48+ months | representation rewrite and barriers | Reject for current roadmap | + +The sizing estimates exclude broad platform qualification. Add 30-50% for Windows, static/musl, uncommon CPU architectures, sanitizer CI, and release engineering. + +## Implementation Guardrails + +1. Preserve the existing lock order: no TC mutex during sweeping; do not take TC after allocation mutex. Audit every new lock against this rule. +2. A source object is copied once or marked once. Assert this in debug builds; never let two workers write a forwarding location or bitmap word without a proven synchronization protocol. +3. Keep all mutator-visible pointer rewriting STW until a complete load-barrier implementation exists. +4. Do not make collect-safe a blanket annotation. It changes object-address lifetime semantics. Test every pointer argument under a forced collection. +5. Treat space_new, immobile/locked objects, multi-segment objects, code objects, weak pairs, ephemerons, guardians, and finalizers as first-class acceptance cases in every new collector mode. +6. Retain and continuously test the serial collector. A debug or feature mode must be able to force it for bisecting heap corruption. +7. Keep object counting and backreference modes correct before optimizing them. They currently select a non-parallel path, so any parallel extension needs an explicit policy rather than accidental activation. + +## Verification Plan + +### Correctness + +- Run the vendored Chez mats suite on every supported machine type. `make gc-mats` provides a fast GC-relevant default over `thread.mo` and `foreign.mo` with telemetry off/on; use `GC_MATS_TARGETS='test-one test-one-safe'` or broader mats targets for release qualification. +- Add randomized graph tests with forced collection at allocation, mutation, remote-queue, and worker-transition points. `make gc-correctness-stress` covers a bounded randomized cross-thread graph with locked objects, weak pairs, guardians, bytevector payloads, and policy cross-checks. It is not a replacement for a future shadow reachability verifier. +- Stress each special reachability feature alone and in combination with cross-thread references and locked objects. `make gc-special-stress` covers weak pairs, ephemerons, guardians, locked objects, combined special objects, and cross-thread combined special objects under default, Phase-2A, descriptor-split, forced-serial, mark-concurrent, region-evacuate, and phase3-observe policy sets. +- Add a shadow reachability verifier in debug builds: after collection, compare live objects reachable from roots with the collector's copy/mark result on bounded heaps. The current branch provides `make gc-shadow-reachability` as a script-level oracle until the C debug verifier exists: it computes expected liveness independently, validates reachable objects through weak references after repeated collections, and runs under all implemented policy modes. +- Run ASan/UBSan/TSan where available. `make gc-sanitizer-smoke` builds isolated sanitizer Chez runtimes and runs reduced shadow, special, and randomized correctness stress. TSan findings around existing deliberate lock-free reads must be documented and suppressed narrowly, never ignored wholesale. +- Run long watchdog tests for blocking FFI, conditions, and contended user mutexes while another thread allocates continuously. +- Use `make gc-qualification` as the local evidence wrapper. The smoke profile is for iteration; use `GC_QUALIFICATION_PROFILE=full GC_QUALIFICATION_INCLUDE_SANITIZERS=1` before promoting any scheduler policy beyond opt-in. + +### Performance acceptance gates + +For the target hardware class, compare against the current main baseline: + +- Phase 1: no collection rendezvous timeout; p99 rendezvous no worse than baseline for nonblocking workloads. +- Phase 2A: at least 15% p99 GC-work reduction on a workload that exercises existing parallel ownership; no more than 3% single-thread throughput loss. +- Phase 2B: at least 30% collector-time reduction on the one-allocator, multi-core workload before productionizing dynamic task splitting. +- Phase 3: at least 50% reduction in old-generation marking time charged to STW, including all barrier overhead in end-to-end throughput. + +Reject an approach if it only improves the average while regressing p99.9 or requires substantially more heap to avoid an allocation failure. + +Use `make gc-baseline-compare` after a matrix run to catch the p99 side of the Phase-2A gate. By default, it labels a row `candidate-win` when `collector_p99_us` improves by at least 15% over the same workload/thread-count default row, and labels p99 regressions above 3% only when the absolute delta is at least 10 microseconds and the candidate row has at least 50 samples. Set `GC_COMPARE_MIN_P99_IMPROVEMENT_PCT`, `GC_COMPARE_MAX_P99_REGRESSION_PCT`, `GC_COMPARE_MIN_P99_REGRESSION_US`, `GC_COMPARE_MIN_SAMPLES_FOR_DECISION`, and `GC_COMPARE_FAIL_ON_REGRESSION=1` for stricter CI-style runs. + +Use `make gc-pause-attribution` after `make gc-baseline-summary` to satisfy the first-ticket dominant-contributor report. It writes `summary-attribution.csv` and uses configurable thresholds: `GC_ATTRIBUTION_RENDEZVOUS_THRESHOLD_PCT`, `GC_ATTRIBUTION_SPECIAL_THRESHOLD_PCT`, `GC_ATTRIBUTION_IMBALANCE_THRESHOLD_PCT`, `GC_ATTRIBUTION_REMOTE_LOCK_THRESHOLD_PCT`, and `GC_ATTRIBUTION_MARK_SWEEP_THRESHOLD_PCT`. + +Use `make gc-throughput` with the same policy matrix to catch the throughput side. It writes `throughput.csv`, `throughput-summary.csv`, and `throughput-compare.csv`; the comparison flags `regress-single-thread-throughput` when a one-thread row loses more than 3% against the same workload's all-default policy row. Set `GC_THROUGHPUT_MAX_REGRESSION_PCT` and `GC_THROUGHPUT_FAIL_ON_REGRESSION=1` for CI-style runs. Treat multi-thread throughput decisions as observations, because the Phase-2A gate is specifically protecting the single-thread fast path. + +Use `make gc-decision-gate` after the baseline, attribution, comparison, and throughput reports exist. It writes `decision-gate.csv`, gives a concise phase-by-phase answer, and keeps long-term work deferred unless the current evidence meets the documented prerequisites. Phase 2A treats multi-thread collector-p99 regressions as hard p99 failures and relies on the throughput comparison for the single-thread fast-path gate. Set `GC_DECISION_FAIL_ON_FAIL=1` only on stable CI or target hardware where performance noise is understood. + +## File Map For The Implementer + +| File | Why it matters | +| --- | --- | +| vendor/ChezScheme/c/gc.c | Core hybrid collector, parallel worker protocol, dirty scan, special reachability | +| vendor/ChezScheme/c/gc-par.c | Builds the parallel inclusion of gc.c | +| vendor/ChezScheme/c/gcwrapper.c | Dispatches among gc-011, object-count/debug, serial, and parallel collectors | +| vendor/ChezScheme/c/alloc.c | Allocation trip threshold and collection request queueing | +| vendor/ChezScheme/c/schsig.c | Global collection-request notification | +| vendor/ChezScheme/c/thread.c | Thread activation/deactivation, mutex, and condition wait behavior | +| vendor/ChezScheme/c/types.h | thread_gc, lock-order, and activation macros | +| vendor/ChezScheme/s/7.ss | High-level collect and STW rendezvous protocol | +| vendor/ChezScheme/mkgc.ss | Generator for object-shape-dependent scanner code | +| vendor/ChezScheme/mats/ | Runtime regression suite; extend it before enabling a new collector mode | + +## First Implementation Ticket + +Create one ticket titled GC pause telemetry and parallel-dispatch baseline. It should change only instrumentation and tests, not the collection algorithm. + +Acceptance criteria: + +1. A collection event reports request time, last-thread-deactivated time, dispatch path, total pause, phase times, worker utilization, remote handoff counts, and heap bytes before/after. +2. The event is allocation-free in collection-critical paths and compiled out or inert by default. +3. A benchmark script produces raw CSV/JSON samples for all Phase-0 workloads. +4. The mats suite and a threaded forced-GC stress test pass with telemetry on and off. +5. The summary includes enough p99 attribution columns to decide whether rendezvous, serial roots/special work, worker imbalance, memory bandwidth, region-barrier pressure, or SATB pre-write barrier pressure is the dominant contributor. The current branch records rendezvous, coarse collector phases, dirty setup, guardians/finalizers, weak pairs, ephemerons, copy-versus-mark source segment counts, descriptor observer/split counts, region observer/selector counts, region preflight/update-edge/reserve/relocation-plan/target-inventory/reservation-map/object-preflight counts, SATB pre-write observer counts including generated-store pre-write records, per-thread candidate buffers, retained queue drain, remark drain, segment-indexed mark-bitmap and bitmap-to-collector preflight records, worker imbalance, remote queues, throughput, CPU, and RSS; a full target-machine run is still required before making the production go/no-go call. + +This branch now implements the first telemetry ticket, the Phase-0 workload/resource baseline, the Phase-1 FFI-hardening slice, the opt-in user-mutex rendezvous experiment, the conservative Phase-2A scheduler experiments, a debug-only Phase-2B descriptor observer plus whole-source-segment split prototype, the first Phase-3A region observer/collection-set selector/cost/preflight/update-edge/reserve/relocation-plan/target-inventory/reservation-map/object-preflight slice, Phase-3B SATB pre-write observer plus retained candidate queues on runtime and generated dirty-barrier store paths, and the first Phase-3C remark/epoch-tagged segment-indexed bitmap mark-state handoff observer plus threaded worker scaffold. `GC_QUALIFICATION_PROFILE=smoke scripts/gc-qualification.sh build/gc-qualification-mark-queue-1784413881` passed all wrapper steps with `default`, `phase2a`, `descriptor-split`, `phase2a-split`, `region-observe`, `region-select`, `mark-satb`, and `mark-queue` baseline presets plus focused `gc-region-stress` and `gc-mark-barrier-stress`. That run passed Phase 0 and Phase 1, reported a smoke-run Phase-2A performance fail (`collector_p99_regressions=1 single_thread_throughput_regressions=2`), kept Phase 2B deferred (`descriptor_split_rows=4 wins=0`), and reported Phase-3A plus Phase-3B evidence (`region_observer_rows=40 selector_rows=20 stores=432000 selected_segments=20 candidate_segments=347 satb_rows=40 satb_stores=456744 satb_queue_candidates=125705 satb_enqueued=62543 satb_drained=62543`). A focused region update-edge smoke at `build/gc-region-update-edge-smoke-1784491186` passed with `region_observer_rows=18`, `selector_rows=12`, `evacuation_rows=6`, `selected_segments=24`, `candidate_segments=106`, `region_candidate_live_bytes_total=1998848`, `region_candidate_scan_cost_bytes_total=1998848`, `region_candidate_evacuation_cost_bytes_total=1998848`, `region_selected_live_bytes_total=655360`, `region_selected_scan_cost_bytes_total=655360`, `region_selected_evacuation_cost_bytes_total=655360`, `region_evacuation_selected_segments=12`, `region_evacuation_eligible_segments=12`, `region_evacuation_blocked_segments=0`, `region_evacuation_inbound_refs_total=0`, `region_evacuation_internal_refs_total=0`, `region_evacuation_outbound_refs_total=0`, `region_evacuation_update_refs_total=0`, and `region_evacuation_table_overflow_total=0`; the region-only Phase-3 gate deferred at `satb_rows=0` after confirming observer, selector, cost-estimate, evacuation-preflight, and update-edge accounting evidence. A focused region reserve smoke at `build/gc-region-reserve-smoke-1784492987` passed with `candidate_live=1376256`, `candidate_scan=1376256`, `candidate_evac=1376256`, `selected_live=524288`, `selected_scan=524288`, `selected_evac=524288`, `evacuation_rows=6`, `evac_selected=12`, `evac_eligible=12`, `evac_blocked=0`, `evac_required_bytes=327680`, `evac_reserve_bytes=2932736`, and `evac_deficit_bytes=0`; the Phase-3 gate now requires nonzero destination-reserve estimates and defers on reserve deficits before real movement can be enabled. A combined mark/region smoke at `build/gc-mark-with-region-evac-smoke-1784491486` passed with `region_observer_rows=6`, `selector_rows=6`, `evacuation_rows=6`, `evac_selected=12`, `evac_eligible=12`, `evac_blocked=0`, `evac_inbound_refs=0`, `evac_internal_refs=0`, `evac_outbound_refs=0`, `evac_update_refs=0`, `satb_rows=18`, `satb_prewrite_stores_total=6103873`, `satb_old_heap_refs_total=1813999`, `satb_queue_candidates_total=1461587`, `satb_queue_enqueued_total=974393`, `satb_queue_drained_total=974393`, `satb_thread_buffered_total=974393`, `concurrent_rows=6`, `satb_concurrent_mark_drained_total=386560`, `satb_remark_drained_total=100616`, `satb_shadow_marked_total=400608`, `satb_shadow_overflow_total=0`, `satb_generated_prewrite_records_total=6018177`, and `satb_dirty_card_records_total=110543`; the Phase-3 gate reached the final defer state with Phase-3A observer/selector/preflight/update-edge evidence and Phase-3B/3C SATB queue/remark/mark-state evidence present, then deferred only because production marker workers and actual partial evacuation are still not implemented. A focused bitmap-handoff smoke at `build/gc-mark-bitmap-smoke-1784492551` passed with `concurrent_rows=6`, `satb_marked_total=399981`, `satb_mark_overflow_total=0`, `satb_mark_bitmap_segments_total=16995`, `satb_mark_bitmap_bytes_total=4350720`, `satb_remark_drained_total=102412`, and `satb_concurrent_mark_drained_total=383872`; the Phase-3 decision gate now requires nonzero segment-bitmap evidence for `mark-concurrent`. A special-reachability Phase-3 gate smoke at `build/gc-phase3-special-gate-smoke-1784492013` passed with `concurrent_special_rows=2`, `concurrent_special_p99_us=3`, `concurrent_guardian_finalizer_p99_us=1`, `concurrent_weak_pair_p99_us=1`, `concurrent_ephemeron_p99_us=1`, `satb_prewrite_stores_total=678616`, `satb_remark_drained_total=35055`, `satb_shadow_marked_total=132717`, and region preflight evidence present; reduced `gc-special-stress` and `gc-correctness-stress` smokes also passed under `mark-concurrent`, `region-evacuate`, and `phase3-observe`. `make gc-satb-barrier-audit` now passes the expanded generated/runtime mutation-path audit, including pair/vector/record/box set and CAS routes plus code-object mutator inventory. An intentional `CHEZ_GC_MARK_QUEUE_CAPACITY=16` overflow run failed as designed. Do not productionize the split prototype, concurrent marking, or partial region evacuation unless target-machine evidence meets the documented gates. Do not start a Java-style concurrent relocating collector until Phase-3A region selection/preflight/reserve/relocation-plan/target-inventory/reservation-map/object-preflight, Phase-3B barrier coverage, and Phase-3C remark/bitmap handoff evidence are complete and measured. + +A focused bitmap-to-collector preflight smoke at `build/gc-mark-apply-preflight-smoke-1784493545` passed with `concurrent_rows=6`, `satb_marked_total=400020`, `satb_mark_bitmap_segments_total=17001`, `satb_mark_bitmap_apply_segments_total=17001`, `satb_mark_bitmap_apply_bits_total=400020`, `satb_mark_bitmap_apply_old_space_bits_total=163228`, `satb_mark_bitmap_apply_mark_mode_bits_total=0`, `satb_mark_bitmap_apply_copy_mode_bits_total=163228`, and `satb_mark_bitmap_apply_skipped_bits_total=236792`; the Phase-3 decision gate now requires bitmap-to-collector old-space preflight evidence before production marker work. This proves the SATB bitmap epoch can be reconciled with the collector's current segment decisions before any real mark-state mutation is enabled. + +A focused owner-local mark-apply smoke at `build/gc-mark-apply-owner-smoke-1784498047` passed with `GC_BASELINE_IN_PLACE_MIN_GENERATION=1`, `mark_experiment=4`, `mark_source_segments=5122`, `satb_mark_bitmap_apply_bits_total=203010`, `satb_mark_bitmap_apply_mark_mode_bits_total=11478`, `satb_mark_bitmap_apply_copy_mode_bits_total=73148`, `satb_mark_bitmap_real_apply_objects_total=402`, `satb_mark_bitmap_real_apply_skipped_bits_total=2486`, and `satb_mark_bitmap_real_apply_failures_total=0`; this proves the opt-in `mark-apply` path can call Chez's generated `mark_object` for owner-local mark-mode objects without applying copy-mode, non-owner, weak/ephemeron, code, record, or other dynamic typed-object bitmap entries. + +A reduced focused mark-barrier stress at `build/gc-mark-apply-stress-smoke-1784498286` passed with the default `gc-mark-barrier-stress` mark preset stack now including `mark-apply`: `mark_apply_rows=3`, `satb_mark_bitmap_apply_mark_mode_bits_total=22806`, `satb_mark_bitmap_real_apply_objects_total=36`, `satb_mark_bitmap_real_apply_skipped_bits_total=2865`, and `satb_mark_bitmap_real_apply_failures_total=0`; the stress wrapper now fails if explicit mark-apply rows do not apply owner-local mark-mode objects or report real-apply failures. + +A focused multi-owner mark-apply smoke at `build/gc-mark-apply-multi-owner-smoke-1784501431` passed with `GC_BASELINE_IN_PLACE_MIN_GENERATION=1`, `mark_apply_rows=2`, `satb_mark_bitmap_apply_mark_mode_bits_total=9001`, `satb_mark_bitmap_real_apply_objects_total=1324`, `satb_mark_bitmap_real_apply_extended_objects_total=0`, `satb_mark_bitmap_real_apply_interior_bits_total=0`, `satb_mark_bitmap_real_apply_skipped_bits_total=48475`, and `satb_mark_bitmap_real_apply_failures_total=0`; this proves the opt-in `mark-apply` path now iterates every mapped sweeper owner context and applies owner-local mark-mode objects beyond the original requesting thread. A later targeted reference-array workload adds the extended-space coverage that this smoke intentionally lacked. + +A reduced wrapper-level multi-owner mark-barrier stress at `build/gc-mark-apply-multi-owner-wrapper-1784501975` passed with `GC_MARK_ITERATIONS=2500`, `GC_MARK_THREADS=4`, `GC_MARK_WORKLOADS='long-lived-dirty cross-owner-graph'`, `mark_apply_rows=2`, `satb_mark_bitmap_apply_mark_mode_bits_total=15701`, `satb_mark_bitmap_real_apply_objects_total=631`, `satb_mark_bitmap_real_apply_extended_objects_total=0`, `satb_mark_bitmap_real_apply_interior_bits_total=0`, `satb_mark_bitmap_real_apply_skipped_bits_total=15001`, and `satb_mark_bitmap_real_apply_failures_total=0`; the wrapper now proves the multi-owner owner-local apply path end-to-end while leaving extended-space coverage to the dedicated `extended-mark-objects` workload. + +A focused extended-space mark-apply smoke at `build/gc-extended-refarray-smoke-1784502655` passed after adding `make-reference-bytevector` to the `extended-mark-objects` workload. With `GC_BASELINE_IN_PLACE_MIN_GENERATION=1`, it recorded `satb_mark_bitmap_apply_mark_mode_bits_total=6638`, `satb_mark_bitmap_apply_extended_old_space_bits_total=482`, `satb_mark_bitmap_apply_extended_mark_mode_bits_total=332`, `satb_mark_bitmap_apply_extended_copy_mode_bits_total=150`, `satb_mark_bitmap_real_apply_objects_total=1009`, `satb_mark_bitmap_real_apply_extended_objects_total=332`, `satb_mark_bitmap_real_apply_skipped_bits_total=31690`, and `satb_mark_bitmap_real_apply_failures_total=0`; this proves the opt-in mark-apply path can apply owner-local extended-space mark-mode entries for reference-array objects. A reduced wrapper-level extended mark-barrier stress also passed with `GC_MARK_ITERATIONS=2500`, `GC_MARK_THREADS=4`, `GC_MARK_WORKLOADS='extended-mark-objects'`, `extended_mark_mode_bits=106`, `real_objects=237`, `extended_objects=47`, `interior_bits=0`, `skipped_bits=12451`, and `failures=0`. + +A focused copy-mode SATB relocation smoke at `build/gc-copy-apply-extended-smoke-1784503070` passed with the record/reference-array `extended-mark-objects` workload and `GC_BASELINE_IN_PLACE_MIN_GENERATION=1`. It recorded `satb_mark_bitmap_apply_mark_mode_bits_total=5685`, `satb_mark_bitmap_apply_copy_mode_bits_total=32552`, `satb_mark_bitmap_apply_extended_mark_mode_bits_total=114`, `satb_mark_bitmap_apply_extended_copy_mode_bits_total=171`, `satb_mark_bitmap_real_apply_objects_total=531`, `satb_mark_bitmap_real_apply_extended_objects_total=104`, `satb_mark_bitmap_real_apply_failures_total=0`, `satb_mark_bitmap_copy_reconcile_objects_total=171`, `satb_mark_bitmap_copy_reconcile_extended_objects_total=171`, `satb_mark_bitmap_copy_reconcile_forwarded_total=171`, `satb_mark_bitmap_copy_reconcile_not_forwarded_total=0`, and `satb_mark_bitmap_copy_reconcile_failures_total=0`; this proves the opt-in copy-mode prototype can relocate eligible SATB bitmap entries through Chez's existing forwarding path before old copy-source segments are cleared. + +A reduced wrapper-level mark-barrier stress at `build/gc-mark-copy-apply-wrapper-1784503089` passed with `GC_MARK_ITERATIONS=2500`, `GC_MARK_THREADS=4`, and `GC_MARK_WORKLOADS='long-lived-dirty cross-owner-graph extended-mark-objects'`. It recorded `mark_apply_rows=3`, `mark_mode_bits=31110`, `copy_mode_bits=193901`, `extended_mark_mode_bits=109`, `real_objects=876`, `extended_objects=59`, `failures=0`, `copy_objects=130`, `copy_extended_objects=130`, `copy_forwarded=130`, `copy_not_forwarded=0`, `copy_interior_bits=0`, `copy_skipped_bits=6539`, and `copy_failures=0`; the wrapper now gates owner-local mark-mode application, targeted extended mark-mode coverage, and copy-mode forwarding reconciliation together. + +A reduced wrapper-level stable extended mark stress at `build/gc-mark-default-after-port-guard-wrapper-1784506335` passed with `GC_MARK_ITERATIONS=2500`, `GC_MARK_THREADS=4`, and `GC_MARK_WORKLOADS='extended-mark-objects'`. It recorded `mark_apply_rows=1`, `mark_mode_bits=9223`, `copy_mode_bits=65360`, `extended_mark_mode_bits=107`, `real_objects=341`, `extended_objects=54`, `failures=0`, `copy_objects=129`, `copy_extended_objects=129`, `copy_forwarded=129`, `copy_not_forwarded=0`, and `copy_failures=0`. The default extended workload remains records, closures, vectors, bytevectors, reference bytevectors, boxes, and strings so the full mark wrapper stays stable. + +A reduced wrapper-level opt-in closed-port region stress at `build/gc-region-optin-port-wrapper-1784506335` passed with `GC_REGION_ITERATIONS=2500`, `GC_REGION_THREADS=4`, `GC_REGION_WORKLOADS='extended-mark-objects'`, and `GC_EXTENDED_MARK_INCLUDE_PORTS=1`. It recorded `candidate_live=1671168`, `candidate_scan=1671168`, `candidate_evac=1671168`, `selected_live=655360`, `selected_scan=655360`, `selected_evac=655360`, `selected=4`, `eligible=4`, `blocked=0`, `target_segments=4`, `reserved_segments=4`, `reserved_bytes=327680`, `map_entries=4`, `object_segments=4`, `object_count=2583`, `object_bytes=41392`, `object_excluded_segments=0`, and `object_failures=0`. This proves closed port objects can be present in the workload while the selected-region policy explicitly excludes `space_port` and `space_code` from evacuation candidates. Live output ports and compiled procedures/code objects are still deliberately not included in the default stress path: live output ports can abort the later `region-evacuate` row, and compiled procedures/code objects crashed `mark-apply` with `nonrecoverable invalid memory reference`. Those failures are recorded in `data/anti-patterns.sexp` as `chez-gc-region-evacuate-port-workload-crash` and `chez-gc-mark-apply-compiled-code-workload-crash`; code-space mark-apply coverage remains implementation work. + +A focused relocation-plan smoke at `build/gc-region-plan-smoke-1784494370` passed with `candidate_live=1458176`, `candidate_scan=1458176`, `candidate_evac=1458176`, `selected_live=524288`, `selected_scan=524288`, `selected_evac=524288`, `evacuation_rows=6`, `evac_selected=12`, `evac_eligible=12`, `evac_blocked=0`, `evac_required_bytes=262144`, `evac_reserve_bytes=2932736`, `evac_deficit_bytes=0`, `plan_source_segments=12`, `plan_target_segments=12`, `plan_copy_bytes=262144`, `plan_update_refs=0`, `plan_metadata_bytes=512`, and `plan_failures=0`; the Phase-3 decision gate and focused region stress now require relocation source/target/copy evidence and defer on plan failures before actual partial evacuation can be enabled. + +A focused target-inventory smoke at `build/gc-region-target-inventory-smoke-1784494946` passed with `candidate_live=1212416`, `candidate_scan=1212416`, `candidate_evac=1212416`, `selected_live=458752`, `selected_scan=458752`, `selected_evac=458752`, `evacuation_rows=6`, `evac_selected=12`, `evac_eligible=12`, `evac_blocked=0`, `evac_required_bytes=262144`, `evac_reserve_bytes=2932736`, `evac_deficit_bytes=0`, `plan_source_segments=12`, `plan_target_segments=12`, `plan_copy_bytes=262144`, `plan_metadata_bytes=512`, `target_available_segments=109`, `target_available_bytes=1785856`, `target_sampled_segments=12`, and `target_inventory_failures=0`; the Phase-3 gate now proves planned destinations come from the normal heap empty-segment inventory, not only from aggregate empty-space accounting. + +A focused target-reservation smoke at `build/gc-region-target-reserve-smoke-1784503796` passed with `GC_BASELINE_POLICY_PRESETS='region-evacuate'`, `GC_BASELINE_WORKLOADS='long-lived-dirty cross-owner-graph'`, `GC_BASELINE_THREADS=4`, and `GC_BASELINE_ITERATIONS=2500`. Both workload rows selected four evacuation regions and planned four targets, then reported ordinary heap target reservation and release with `reserved=4`, `reserved_bytes=65536`, `inv_fail=0`, `reserve_fail=0`, `unreserve_fail=0`, and `plan_fail=0`. A reduced wrapper-level region stress at `build/gc-region-target-reserve-wrapper-1784503825` passed with `GC_REGION_ITERATIONS=2500` and `GC_REGION_THREADS=4`; after regenerating the summary with total reserved bytes, the Phase-3 gate emitted `target_reserved_segments=12`, `target_reserved_bytes=393216`, `map_entries=12`, `object_segments=12`, `object_count=2026`, `object_bytes=33632`, and `satb_rows=0`. This proves the debug preflight can temporarily detach planned ordinary heap destination segments from `chunk->unused_segs`, restore them without loss, and keep the source-to-target map aligned with the claimed reserve. It still does not keep those targets reserved across object movement. + +A reduced wrapper-level persistent-target reservation stress at `build/gc-region-persistent-target-wrapper-1784504873` passed with `GC_REGION_ITERATIONS=2500` and `GC_REGION_THREADS=4`. It reported `selected=12`, `eligible=12`, `target_segments=12`, `reserved_segments=12`, `reserved_bytes=393216`, `reservation_failures=0`, `unreserve_failures=0`, `map_entries=12`, `object_segments=12`, `object_count=3323`, `object_bytes=53168`, `object_excluded_segments=0`, and `object_failures=0`. This proves the debug preflight now keeps sampled ordinary heap destinations detached from `chunk->unused_segs` until telemetry finish and releases them before emitting the event, while preserving reservation-map and selected-object invariants. The remaining destination work is to hand those leased segments to a real evacuation allocator instead of releasing them unused. + +A reduced wrapper-level target-allocator stress at `build/gc-region-target-allocator-wrapper-1784506881` passed with `GC_REGION_ITERATIONS=2500` and `GC_REGION_THREADS=4`. It reported `selected=12`, `eligible=12`, `target_segments=12`, `reserved_segments=12`, `reserved_bytes=327680`, `allocator_segments=12`, `allocator_capacity_bytes=327680`, `allocator_used_bytes=327680`, `allocator_waste_bytes=0`, `allocator_cursor_bytes=16384`, `allocator_failures=0`, `map_entries=12`, `object_segments=12`, `object_count=4196`, `object_bytes=67264`, `object_excluded_segments=0`, and `object_failures=0`. This proves the debug preflight now prepares bounded bump-allocation cursors over the leased ordinary heap targets and verifies the planned copy bytes fit before object movement is enabled. The remaining destination work is to copy selected objects into those cursor ranges, publish forwarding metadata, and rewrite references. + +A reduced wrapper-level copy-schedule stress at `build/gc-region-copy-schedule-wrapper-1784507764` passed with `GC_REGION_ITERATIONS=2500` and `GC_REGION_THREADS=4`. It reported `selected=12`, `eligible=12`, `target_segments=12`, `reserved_segments=12`, `reserved_bytes=393216`, `allocator_segments=12`, `allocator_capacity_bytes=393216`, `allocator_used_bytes=393216`, `allocator_failures=0`, `map_entries=12`, `object_segments=12`, `object_count=5247`, `object_bytes=84080`, `object_excluded_segments=0`, `object_failures=0`, `copy_schedule_objects=5247`, `copy_schedule_bytes=84080`, `copy_schedule_target_segments=6`, `copy_schedule_cursor_bytes=16384`, `copy_schedule_waste_bytes=63376`, and `copy_schedule_failures=0`. This proves the debug preflight can dry-run selected-object placement through the leased-target cursors and detect mismatch/failure before enabling actual object copying. The remaining destination work is to replace the dry-run schedule with real object copies, publish forwarding metadata for the copied starts, and rewrite references to the copied collection set. + +A reduced wrapper-level copy-entry metadata stress at `build/gc-region-copy-entry-wrapper-1784508344` passed with `GC_REGION_ITERATIONS=2500` and `GC_REGION_THREADS=4`. It reported `selected=12`, `eligible=12`, `target_segments=12`, `reserved_segments=12`, `reserved_bytes=393216`, `allocator_segments=12`, `allocator_capacity_bytes=393216`, `allocator_used_bytes=393216`, `allocator_failures=0`, `map_entries=12`, `object_segments=12`, `object_count=5241`, `object_bytes=83984`, `object_excluded_segments=0`, `object_failures=0`, `copy_schedule_objects=5241`, `copy_schedule_bytes=83984`, `copy_schedule_target_segments=6`, `copy_schedule_failures=0`, `copy_entry_objects=5241`, `copy_entry_bytes=83984`, `copy_entry_metadata_bytes=125784`, and `copy_entry_failures=0`. This proves the debug preflight now derives bounded source/destination/size relocation-entry metadata for every scheduled selected object. The remaining destination work is to drive real object copies from those entries, install forwarding metadata, and rewrite references.