ReviewOS

zig-utils/zig-gc

Perf: use atomic header guards for owned slab publication

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

Parent downstream: zig-js #97 Related: zig-gc #29, zig-js #128

The owned-cell publication bitmap fixed classifiers observing constructing/reused slab headers, but it adds a second size-class lock acquisition after every allocation batch and one unpublication lock per dead cell before finalization. A fresh eight-lane profile at zig-js daf74724 / zig-gc 092d8d7 still places batch publication, swept-slot handling, and sweepPhaseLocked among the dominant stacks. Batch unpublication was previously rejected because holding the bucket lock across a whole dead run made peer classification latency worse.

Design

  • Let an owned backing initialize the header guard word to zero while the slot is still protected by its allocation bucket lock.
  • Route single-cell owned allocations through the same guarded allocation hook, so no issued owned slot ever exposes uninitialized header storage.
  • Make header magic an atomic acquire/release liveness guard: publish it only after immutable header fields are initialized; clear it before finalization/reuse.
  • Let owned classifiers map issued slots without a separate publication bitmap; zig-gc validates the guard before reading size/kind or treating the pointer as live.
  • Preserve generic allocator bindings that do not provide exact owned/guarded storage.

Acceptance

  • Exact construction/reuse/classification tests prove issued-but-zero slots classify empty and fully initialized headers classify live.
  • Remove the downstream batch-publication and per-cell-unpublication bucket-lock work without weakening TSan ordering.
  • Pass full zig-gc tests and focused downstream GC/OOM/no-GIL/TSan gates.
  • Exact-parent downstream A/B must avoid one/two-lane regressions and materially improve four/eight-lane shared object churn; otherwise fully revert and document.
9 days ago

The atomic-header-guard prototype was implemented across zig-gc and zig-js, correctness-tested, measured in an exact-parent A/B, and fully reverted. No code is retained.

What was tested:

  • cell-only backing batches zeroed an atomic guard and marked the slot safe to inspect while still holding the size-class lock;
  • all single GC cells routed through that guarded hook, while generic side-storage allocations remained excluded;
  • zig-gc release-published header magic after immutable header initialization and cleared it before finalization;
  • guarded slots skipped the later publication lock and per-cell unpublication lock;
  • a new construction/reuse test proved zero guards, issued-slot classification, cleared ownership, and re-zeroing on reuse;
  • full zig-gc tests and the focused cooperative zig-js suite passed with zero leaks.

Seven-pair order-balanced downstream A/B (shared object_churn, 100 jobs, exact checksums), parent -> candidate medians:

  • 1 lane: 218.158 -> 219.650 ms (+0.7%);
  • 2 lanes: 497.794 -> 448.008 ms (-10.0%);
  • 4 lanes: 1,033.232 -> 1,116.110 ms (+8.0%);
  • 8 lanes: 1,516.755 -> 1,497.675 ms (-1.3%).

The result is mixed and fails acceptance: four lanes regress, eight lanes do not materially improve, and dispersion remains high. Removing the second publication/unpublication lock is not enough to overcome the extra atomic/header-guard work and does not solve collector serialization. Closing as a documented negative result.

Sign in to comment on this issue.