ReviewOS

zig-utils/zig-js

Perf: collect the nursery at single-mutator JS safepoints

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

Parent: #62 Related: zig-utils/zig-gc#4, zig-utils/zig-gc#5

Problem

GC-enabled single-mutator execution enables zig-gc nursery collection, but the previous mid-script driver ignored the young-generation threshold and started full incremental marking from total live bytes. Allocation-heavy loops therefore retraced old built-ins and long-lived objects instead of reclaiming short-lived young cells through a minor cycle.

Implemented scope

  • run a stop-the-world minor collection at a single-mutator JS safepoint when the nursery threshold is reached and no full/concurrent cycle is active;
  • decide new full cycles from tenured bytes with shouldCollectOld;
  • preserve native-stack and parked-stack root publication around the minor pause;
  • retain parallel-mutator safety rules;
  • use the 4 MiB zig-gc nursery policy from zig-utils/zig-gc#5;
  • avoid repeated exact-ownership bucket scans and immutable promoted-function rescans.

Acceptance criteria

  • A running JS loop triggers multiple minor collections before returning to a quiescent boundary.
  • The same loop avoids full collections when tenured pressure is held below threshold.
  • Exact results and retained young graphs survive minor collection.
  • Existing incremental, concurrent, parallel, and full collection tests remain correct.
  • The bounded object_churn checksum remains 1030552128 and direct throughput materially improves.
  • Focused GC tests, zig-gc tests, benchmark harness tests, and the 751-test zig-js suite pass.
  • Run the final TSan gate with the eventual publication batch.

Landed commits

  • a29d23d — collect nursery at mutator safepoints
  • abdc30b — skip immutable promoted-function rescans
  • zig-utils/zig-gc@a0b9101 — size nursery for mutator checkpoints
9 days ago

Pushed the complete checkpoint to main.

Validation:

  • zig-gc: zig build test passed
  • zig-js: 751/751 passed, 0 skipped, 0 failed, 0 leaked
  • comparison harness: 7/7 passed
  • ReleaseFast object_churn, single context, 100 jobs x 7 samples: median 1,141,386,292 ns; range 938,623,417–1,255,558,458 ns; checksum 1030552128
  • retained pre-checkpoint median: about 1.468 s, so this checkpoint is about 22.3% faster
  • same-run JSC median: 120,900,125 ns, so the remaining direct gap is about 9.44x

The final TSan/publication gate remains open.

9 days ago

Measurement correction: the previously posted 1.141 s / 9.44x object_churn result came from an installed comparison runner whose timestamp predated the checkpoint commits; zig build -Doptimize=ReleaseFast does not rebuild that runner. Disregard that performance number, while retaining the reported 751/751, zig-gc, and harness validation results.

After rebuilding with zig build benchmark-comparison-bin, the new main checkpoint 8e861ec2 (#64) measures 871.043 ms median for zig-js versus 120.567 ms for JSC over 100 jobs x 7 samples, exact checksum 1030552128 (7.22x remaining). Alternating clean abdc30bf and current runners measured 867.854 ms versus 824.063 ms, about a 5.0% improvement attributable to #64. The full suite again passed 751/751 with no skips/failures/leaks; the final TSan gate remains open here.

9 days ago

Related root-scan checkpoint from #66: zig-gc 51c491e plus zig-js ecd5bb4 removes the generic sorted allocation-address index from owned-cell conservative scanning. On object_churn, sort.block fell from 98 sampled leaves to 0 and Visitor.markConservativeWord from 73 to 23; the clean pinned A/B improved 13.3 percent with the exact checksum preserved.

This does not close #63. Its remaining nursery-safepoint and final TSan acceptance work stays tracked here.

9 days ago

The first batched publication TSan gate did not pass. CI run 29327767669 found two unsuppressed no-GIL races in semantics/proto-cycle-race.js and objectmodel/i08-named-vs-indexed-first-install.js; the failure is now scoped in #71 with exact stacks and acceptance gates. Unit, benchmark-harness, test262-parallel, ordinary thread, threadfuzz, and the other TSan jobs in that run passed. This issue correctly remains open until #71 and the final TSan batch are green.

9 days ago

#71 now has a focused fix on main in c3153b1. Both formerly racing corpus cases pass locally under a TSan binary with halt_on_error=1, and full units are 759/759 with zero leaks plus 7/7 harness tests. #63 remains open until the fresh full main CI completes the complete TSan publication gate.

9 days ago

Publication update: run https://github.com/zig-utils/zig-js/actions/runs/29352841839 did not clear the final gate because tsan-threadfuzz-lifecycle hit an aggregate watchdog timeout. Direct logs showed the TSan thread-leak reports were secondary to forced process exit, not a new engine race.

The harness fix is tracked in #93 and pushed as 1e7298b4; focused lifecycle seed 1 is locally TSan-clean (56/56, 46.8s). Fresh full gate: https://github.com/zig-utils/zig-js/actions/runs/29353934622

Keeping #63 open pending that full green run.

9 days ago

Follow-up: the fresh tsan-threadfuzz-lifecycle publication job is green on 1e7298b4: 112 programs, 0 failures, 130263 ms, with no watchdog or TSan diagnostics. #93 is closed.

Job: https://github.com/zig-utils/zig-js/actions/runs/29353934622/job/87156822910

#63 remains open pending the complete publication run.

9 days ago

The 1e7298b4 publication run exposed a separate bounded-heap attrs/accessor key ownership bug in tsan-unit (shard 1/4), tracked as #94. The fix is pushed as f4602d86 and the focused pressure tests are TSan-clean (3/3, zero leaks).

#63 remains open pending a fresh complete publication run.

9 days ago

Fresh publication run for f4602d86: https://github.com/zig-utils/zig-js/actions/runs/29355087082

9 days ago

CI update: the previously failing tsan-unit shard 1/4 is green on run 29355087082, so #94 is closed. The same run exposed a separate threadfuzz-midgc seed-20 termination-reaction collection-progress failure; tracked independently in #96 with the exact command and log evidence. New main run for db131e0: https://github.com/zig-utils/zig-js/actions/runs/29356365753

9 days ago

The documentation push superseded the db131e0 run. The current exact main run is fdb32d8: https://github.com/zig-utils/zig-js/actions/runs/29357367045. It is queued; use this run for the final TSan/no-GIL gate.

9 days ago

The refreshed clean-commit comparison at 6b27f4d preserves all 1,540 samples from b8a4973. Direct zig-js geometric-mean throughput remains 2.38x JSC across the ten-workload matrix; the allocation-heavy exception is now 195.331 ms vs JSC 116.085 ms. Shared object churn improved absolutely but still scales 0.21x at eight lanes, so nursery/collector scaling work remains relevant here and under #97.

9 days ago

The final publication/TSan acceptance gate is now green on main: https://github.com/zig-utils/zig-js/actions/runs/29385360583 — 32 successful jobs, 1 expected skipped nightly entry, 0 failures. This includes all four suppression-free TSan no-GIL corpus shards, full unit/TSan matrices, both mid-GC profiles, lifecycle fuzz, benchmark harness, docs, and Test262. The implemented nursery-at-safepoint scope and every acceptance item are complete; further nursery sizing/scaling work will be tracked separately with fresh benchmark evidence.

Sign in to comment on this issue.