ReviewOS

zig-utils/zig-gc

Perf: reduce synchronized nursery sweep pressure across independent heaps

#24
Closed chris opened this 9 days ago ·
9 days ago

Parent consumer task: zig-utils/zig-js#62 Related: zig-utils/zig-js#97, zig-utils/zig-js#116

Evidence

The July 15 clean zig-js/JSC publication leaves object_churn as the only independent-context loss: at eight warmed contexts zig-js is 277.124 ms versus JSC's 172.070 ms, and at eight cold contexts it is 351.305 ms versus 179.788 ms. Direct single-context now wins at 114.315 ms versus 119.322 ms.

A matched current-main profile of the exact warmed eight-context row shows Heap.collectYoung and sweepPhaseLocked dominating aggregate worker CPU; the single-context profile spends proportionally more time in the allocation loop itself. Each independent heap owns its collector and cell backing, so this is not shared-heap locking. Every heap currently starts and low-survival workloads settle at the same 4 MiB nursery floor, causing identical workers to enter expensive minor cycles at nearly the same cadence.

Scope

  • Measure private heaps with multiple nursery sizes and/or a general adaptive policy; do not special-case zig-js or a benchmark workload.
  • Prefer per-heap sizing based on observable survivor/allocation/collection behavior over process-global state.
  • Preserve the existing one-cycle nursery invariant, exact promoted/reclaimed accounting, remembered owners/targets, weak processing, finalization ordering, OOM behavior, and immediate survivor tenure.
  • Record direct, warmed 8-context, and cold 8-context wall time plus peak footprint; reject a throughput-only trade with unbounded memory growth.
  • If a fixed larger floor is best, justify the production memory/latency trade across small and large heaps. If no size is a robust win, close the experiment with the full measurements.

Acceptance

  • Materially improve eight-context low-survival allocation throughput without regressing direct throughput or context lifecycle materially.
  • Keep memory growth bounded and document the chosen pause/footprint tradeoff.
  • Pass focused threshold/adaptation/accounting tests, the full zig-gc suite, and downstream zig-js GC/TSan gates.
  • Land the dependency change first, then update zig-js with the exact zig-gc commit and fair benchmark evidence.
9 days ago

Closing this as a duplicate of #21. The fixed nursery-size direction was already evaluated there and rejected; this follow-up reproduced that conclusion under the current independent-context benchmark.

Exact order-balanced screening against the unchanged 4 MiB baseline (7 samples per case, identical checksums):

  • 8 MiB: direct was mixed (about -1% to +4%), warmed 8-context throughput was 2-14% slower, and cold 8-context throughput was 5-12% slower.
  • 2 MiB: warmed 8-context throughput improved by 13-34%, but direct throughput regressed by 12-28% and cold throughput was mixed to worse.

The source was restored to 4 MiB and no commit was made. A fixed global nursery size is therefore not the right lever; the next work should target the measured synchronized collection/sweep and allocator overhead without trading away direct or cold performance.

Sign in to comment on this issue.