ReviewOS

zig-utils/zig-js

Perf: remove shared-realm object-churn GC scaling collapse

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

Parent: #62 Related: #44, #49, #116, #118, #119, #120, #131

The accepted July 17 structured-stack matrix remains the latest complete zig-js/JavaScriptCore publication:

  • measured zig-js: 01fcf42c
  • report and raw 1,540-sample evidence
  • shared 1/2/4/8 object-churn medians: 190.969 / 293.039 / 683.870 / 1,651.879 ms
  • shared scaling: 1.00x / 1.30x / 1.12x / 0.92x

The accepted amortized-publication A/B improved the then-current contended rows without changing the lone-worker path. The issue remains open because the strict 1.0x eight-lane scaling target is not yet met.

Tracked follow-up

  • #426 — exact cooperative-GC phase attribution: sweep is 87.4% of the eight-lane pause.
  • #427 — whole-run backing reclamation rejected and fully reverted after its exact A/B.

Acceptance

  • Profile the exact shared object-churn row with collector, safepoint, allocation, backing, and lock evidence.
  • Preserve object prototypes, initialized properties, indexed replacement, displaced reads, barriers, OOM behavior, exact checkpoints, and checksums.
  • Materially improve the formerly sub-0.65x eight-lane result.
  • Keep every shared lane at or above 1.0x scaling with repeated low-dispersion evidence.
  • Avoid direct/independent-path regressions by retaining their allocation policy.
  • Pass dependency normal+TSan, full zig-js units, focused TSan, and exact-parent A/B for retained changes.

Completion evidence — July 29, 2026

The final clean-tree publication measures runtime e690a866 with the steady shared harness at a7cf92c7, published by e4187c0f:

  • shared object-churn scaling: 1.26x / 1.16x / 1.20x at 2/4/8 lanes;
  • dispersion: 6.26% / 11.31% / 2.82% RSD at 2/4/8 lanes;
  • exact-parent reserve A/B: 59,200 → 1,600 publication batches, 4.83x faster at eight lanes, identical checksums, 1.001x RSS;
  • validation: zig-gc normal/ReleaseSafe/TSan, 1,460/1,460 zig-js units, focused TSan, benchmark publication gates, link check, and docs build.

Evidence: final report, 1,540 raw samples, and causal reserve A/B. This closes the sole remaining child of #49 and leaves #44 only the directly comparable independent object-churn rows.

Latest diagnosis

The #426 phase profile rules out rendezvous and tracing as the eight-lane collector cost. The #427 exact A/B then rules out backing bitmap/free-slot transitions alone: sweep improved only 0.7% while wall time regressed 0.9%, so both prototypes were reverted. Do not retry the rejected wait, threshold, unpublication, owned-enumeration, pressure-accounting, or whole-run-backing designs without new evidence. README scores remain tied to complete matrices only.

9 days ago

Fresh exact-path sample on fdb32d8 (ReleaseFast, shared object_churn, 8 lanes) confirms the first concrete bottleneck. Across the eight worker stacks, top-of-stack samples were dominated by managed-pointer classification and allocation metadata: Heap.headerForPayload 9,523; Heap.create 8,390; __ulock_wait2 2,842; GcCellBacking.allocFn 495; Object.prototype lookup 362. The one-lane shared median is already about 4.8x the direct median because the fixed-shape allocation loop is deliberately disabled whenever parallel IC synchronization is active, forcing per-op VM dispatch, property classification, literal initialization, and generic barriers. I am testing a parallel-safe version of that existing general quick path: seqlock-only literal-shape reads, property/element lock snapshots, exact managed barriers, and generic store fallback for observable/exotic races.

9 days ago

The VM-side slice is now published as #98 in b8a4973, with refreshed evidence in 6b27f4d.\n\nFull saved shared-realm object-churn medians improved:\n- 1 lane: 986.746 ms -> 488.792 ms\n- 8 lanes: 28,321.516 ms -> 18,472.129 ms\n\nThat is substantial absolute progress, but mode-local eight-lane scaling is now 0.21x because the one-lane path improved more. The quick path is no longer the primary blocker; the next slice remains open here and should target Heap.create / header lookup / backing allocation and collector serialization observed in the profile.

9 days ago

Batched publication slice landed and is published.

Implementation:

  • zig-gc: 65441fe, 3183d08, 179ffe2 under zig-gc#15
  • zig-js consumer: 84d2f9f4
  • calibrated full-matrix harness: ce652645, 3d790a60 under #99
  • report/README/raw evidence: 3cc2a58c

Validation:

  • Focused semantic/GC and TSan tests pass; the real four-Thread test proves >=2,048 object cells use batch publication.
  • Full zig-js suite: 761/761, zero leaks. Comparison harness: 7/7.
  • Exact-parent direct A/B: 195.410 -> 192.974 ms (-1.25%, 5/7 pair wins), exact checksum.
  • Exact-parent shared 8-lane A/B: 902.841 -> 393.507 ms (-56.42%, 7/7 pair wins), exact checksum.

Validated publication (1,540 samples, commit 3d790a60):

  • shared 1 lane: 475.705 ms, 1.00x
  • shared 2 lanes: 959.634 ms, 0.99x
  • shared 4 lanes: 4,867.850 ms, 0.39x
  • shared 8 lanes: 12,368.464 ms, 0.31x

Versus the previous publication, the 8-lane median falls 33.0% (18,472.129 -> 12,368.464 ms) and scaling improves 48% (0.21x -> 0.31x). The direct row remains the sole JSC loss at 0.59x zig-js/JSC throughput. Post-change sampling moves the dominant cost away from per-cell allocation-lock acquisition toward header classification, batch/default initialization, and backing allocation.

Keeping #97 open: acceptance requires every shared lane above 1.0x, and 4/8 lanes remain below it.

Report: https://github.com/zig-utils/zig-js/blob/main/docs/.data/benchmark-comparison-2026-07-14.md Raw: https://github.com/zig-utils/zig-js/blob/main/docs/.data/benchmark-comparison-2026-07-14.tsv

9 days ago

Final validation update: zig-gc unit + 20x TSan is green at https://github.com/zig-utils/zig-gc/actions/runs/29364720539, and zig-js main CI is green on attempt 2 at https://github.com/zig-utils/zig-js/actions/runs/29364341824.

All implementation/validation gates for this batch-publication slice passed. #97 remains open solely because the performance acceptance target (>1x shared scaling at every lane) is not yet met.

9 days ago

Fresh full publication from clean measured commit c98a36c4, published by e4b72407:

The exact-parent A/B for #101 measured 12.2% faster shared8 and 3.4% faster direct with exact checksums. #101 is complete; object churn remains the sole direct JSC loss and shared lanes above 1x remains unmet, so the parent performance issues stay open.

9 days ago

#102 is complete on main: per-chunk reusable-slot bitmaps removed intrusive dead-cell allocator writes. Exact-parent object_churn improved 7.37% direct and 1.09%/2.51%/13.95%/2.96% at shared 1/2/4/8 lanes, with exact checksums. The refreshed 1,540-sample publication is in 24656d74; README/docs now report a 2.56x direct geometric-mean lead over JSC and 4.09x shared 8-lane scaling.

9 days ago

Checkpoint-fallback slice #104 is now on main (2dac7aca, docs/raw evidence ab386010). It materially improves direct and independent-context object churn (exact-parent: +16.57% direct, +20.39% warmed 8-lane, +9.02% cold 8-lane), but intentionally does not change shared/concurrent-GC checkpoint behavior after the shared prototype regressed. The new full shared 1/2/4/8 medians are 377.316 / 741.069 / 2,316.414 / 9,789.216 ms (0.31x eight-lane scaling), so this parent remains open and the shared allocator/GC convoy remains the primary target.

9 days ago

#104 shared checkpoint batching is now on main (5f891bc8, publication 409d6a30). The new full shared object-churn medians are 351.344 / 717.910 / 2,112.202 / 9,311.238 ms at 1/2/4/8 lanes, improving published throughput by 7.4% / 3.2% / 9.7% / 5.1%. Matched profiles removed 79.5% of generic Heap.create and 74.5% of headerForPayload samples. Eight-lane scaling is still only 0.30x, so this parent remains open; the next profile should focus on the now-dominant GC backing allocation/lock path rather than the eliminated generic fallback convoy.

9 days ago

#105 landed and the complete benchmark publication is now on main.

The new zig-gc binding hook batches raw cell slabs under one GcCellBacking size-class lock, while zig-gc retains private header initialization and one-lock metadata publication. Commits: zig-gc d35570e, zig-js 0679295, docs/raw publication 1391131.

Shared object-churn medians are now 350.226 / 676.030 / 1403.741 / 7658.643 ms at 1/2/4/8 lanes. Relative to the prior publication, throughput improved 0.3% / 6.2% / 50.5% / 21.6%; focused A/B measured neutral direct performance and 3.3% / 35.4% / 14.8% lower 2/4/8-lane shared wall time with exact checksums.

The parent remains open: 8-lane object churn is still only 0.37x scaling and the direct object-churn row still loses to JSC.

9 days ago

Progress: #106 is implemented on main as 6ec1fbd. Exact A/B reduces direct object-churn wall time by ~24.4% and shared-heap 1/2/4/8-thread wall time by ~38–40%, with matching checksums and the full 765-test suite green. A single full publication run is in progress; README.md and the detailed benchmark report will be refreshed from it.

9 days ago

#106 is fully published on main through 8dce8da. README.md and the detailed 1,540-sample report now show the object-churn direct gap narrowed to 2.3% (121.638 ms vs JSC 118.874 ms), while the exact A/B attributes a 24.4% direct and 38–40% shared improvement to weak-pass gating. The aggregate direct lead is now 2.74x across the ten-workload matrix. Shared object-churn scaling (0.30x at eight lanes, high dispersion) remains the next optimization target, so #97 stays open.

9 days ago

Validation complete for the weak-pass gating slice and its CI-discovered ordering follow-up: https://github.com/zig-utils/zig-js/actions/runs/29381215887 finished with 32 successful jobs and 0 failures, including both mid-GC fuzz profiles and all TSan no-GIL corpus shards. #106 and #107 are closed; #97 remains open for the remaining 2.3% direct object-churn gap and shared-heap scaling work.

9 days ago

#109 / zig-gc#20 are now on main: bounded same-size sweep-release batches remove per-cell backing-lock churn. The clean publication moves shared object churn to 192.868 / 350.919 / 730.610 / 5192.344 ms at 1/2/4/8 lanes, improving 10.9% / 15.7% / 12.2% / 10.5% over the prior report. Direct object churn is 117.481 ms vs JSC 116.026 ms (1.3% behind). Shared scaling remains 0.30x and 4/8-lane RSD remains high, so this parent stays open.

9 days ago

Final gate for the #109 same-size sweep batching slice and #110 oracle hardening is fully green: https://github.com/zig-utils/zig-js/actions/runs/29385360583 — 32 successful jobs, 1 expected skipped nightly entry, 0 failures. #109 and #110 are closed. This parent remains open: clean publication still has direct single-thread object churn 1.3% behind JSC, and shared-object churn scales only 0.30x at 8 lanes despite the 10.5–15.7% publication improvements and 30.5% exact 4-lane A/B win.

9 days ago

Latest published shared object-churn rows from 86d0c5b: 187.078 / 420.071 / 2250.007 / 7763.931 ms at 1/2/4/8 lanes, exact checksums, with 0.19x eight-lane scaling and 21.96% RSD at eight lanes. The capture was on battery/discharging and is not directly delta-compared with the previous AC report. Batched heap accounting passed normal and full TSan gates but does not solve shared-realm allocation contention; #97 remains open.

9 days ago

The July 15 clean publication records the current shared-realm baseline after #118:

This is materially better than the July 14 7,763.931 ms row, but the power source changed around the run and the row remains highly variable, so no small cross-run causal claim is made. #118 is deliberately disabled for shared/GIL contexts and therefore does not claim this as its effect. The exact profile from #116 still identifies continuous allocation without a convergent mid-script collection as the main footprint/host-boundary problem.

9 days ago

The #120 256-byte object-slab work is published in 6c71f46f from clean measured source 0a74f7d1. Shared object-churn now records 123.180 / 246.548 / 463.608 / 1,922.828 ms at 1/2/4/8 lanes, with 1.00x / 1.00x / 1.06x / 0.51x scaling and 4.33% eight-lane RSD.

This materially lowers eight-lane wall time from the preceding published 2,374.329 ms and closes every direct/independent JSC gap, but it does not close this issue: eight-lane shared scaling remains below 1.0x because the one-lane path improved even more. The body now carries the current baseline and directs the next profile at allocation/backing/sweep serialization on the new 256-byte layout.

9 days ago

New exact profile and dependency work: the July 15 256-byte layout sample resolved the dominant allocation-batch address to zig-gc's repeated failed alloc_lock CAS. zig-gc #26 / decff85 replaces that write storm with test-and-test-and-set read spinning plus a bounded 64-spin yield. Seven-sample exact-parent downstream medians improved 8-lane shared object_churn from 1,319.888 ms to 1,164.047 ms (11.8% lower); pure read-spin, 16-spin yield, and 256-spin yield were all inferior. A post-change 15-second profile confirms libc/chunk growth remain negligible and the remaining allocation wait is mostly deliberate handoff/yield, so the next optimization must reduce shared publication frequency/metadata, not retune this lock. The strict >=1.0x scaling checkbox remains open pending the next full publication.

9 days ago

Opened zig-gc#28 for the next measured dependency slice: explicitly registered per-mutator metadata shards. This follows the #26 profile and the rejected zig-gc#27 exact A/B: one global lock-free pending stack merely moved the convoy to a global CAS word and regressed 4/8 lanes by 17.0%/12.6%. #28 requires private publication with quiescent collection/thread-exit ownership, full normal+TSan safety, and an order-balanced downstream A/B that rejects any 1/2-lane regression and must materially improve both 4 and 8 lanes.

9 days ago

zig-gc#28's registered per-mutator metadata-shard prototype was implemented, safety-tested, measured, and fully reverted.

It passed 37/37 zig-gc tests and 16/16 focused zig-js parallel-GC tests with zero leaks. Every A/B checksum matched. However, the order-balanced seven-pair shared object_churn screen was decisively negative:

  • 1 lane: 103.054 -> 97.920 ms (-5.0%);
  • 2 lanes: 125.061 -> 159.177 ms (+27.3%);
  • 4 lanes: 369.602 -> 867.557 ms (+134.7%);
  • 8 lanes: 997.493 -> 2,703.501 ms (+171.0%).

This rules out deferred private all-list chains: removing the publication hotspot at the cost of cross-mutator collector/list locality is far worse at scale. Together with rejected zig-gc#25/#27, the evidence says the next slice must change collector metadata/traversal structure, not merely change how batches reach the same intrusive list.

9 days ago

Fresh full 1,540-sample matrix at zig-js e2384dcf / zig-gc 092d8d7 validates the publication-batch direction but does not yet satisfy this issue, so I am not publishing it over the accepted README report. Shared object_churn: 129.641 / 287.712 / 553.650 / 1,500.087 ms at 1/2/4/8 lanes, scaling 1.00x / 0.90x / 0.94x / 0.69x. Eight lanes improves 22.0% from 1,922.828 ms and clears the >0.65 target, but 2/4 lanes remain below the required 1.0x. The run also had an anomalous cold-2 median (177.319 ms, 29.42% RSD, min 89.660) and is unsuitable as a replacement publication. Next concrete target: batch owned-slot unpublication before sweep finalization, matching the successful allocation publication batching rather than taking one bucket lock per dead cell.

9 days ago

Rejected follow-up: batching sweep unpublication before finalization (zig-gc + zig-js prototype, 36/36 heap and 16/16 parallel-GC tests green) made performance materially worse, likely because it extends the bucket-lock hold across address lookup for an entire dead-cell run while peer classifiers wait. Seven-sample focused medians regressed to roughly 166/351/745/1,764 ms at 1/2/4/8 lanes from the full-run 130/288/554/1,500 ms. Both worktrees were fully reverted; nothing was committed. Per-cell unpublication is not the remaining lever.

9 days ago

Rejected local tranche re-calibration screen after the 256-byte slab and batched publication work; the tracked worktree is restored and nothing is committed.

ReleaseFast shared object_churn, 100 jobs, seven samples per 1/2/4/8-lane process, exact checksums:

  • shipping 1 GiB: 130.752 / 266.487 / 548.519 / 1,321.864 ms;
  • 2 GiB: 141.258 / 277.530 / 716.050 / 1,521.819 ms;
  • 512 MiB: 140.578 / 293.700 / 599.755 / 1,500.589 ms.

The 2 GiB candidate removes some rendezvous frequency but loses more to the larger live heap and a severe four-lane tail. The 512 MiB candidate shortens each sweep but loses more to extra rendezvous work. Both directions regress every contended lane in this screen; 1 GiB remains the correct current policy. The next slice must change sweep/metadata structure or parallelism, not retune the cooperative byte threshold.

9 days ago

Fresh restored-binary profile and rejected wait-path prototype; tracked worktree is clean and nothing is committed.

A 10-second macOS sample of the exact eight-lane kernel (daf74724 / zig-gc 092d8d7) recorded these collapsed top stacks across the worker set:

  • scheduler swtch_pri: 14,619;
  • joinCooperativeGcRequest: 1,924;
  • allocObjectBatch: 1,908;
  • sweepPhaseLocked: 629;
  • freed-slot lookup: 624;
  • batch publication: 594;
  • collectYoung: 433.

I replaced the cooperative peer/gate yield loops with a race-safe std.Io futex epoch: peers published and froze roots exactly as before, atomically slept only while the request remained active, and the collector/abandoned-owner paths advanced+woke the epoch when clearing the generation. The focused cooperative suite passed 3/3 with zero leaks, including abandoned-collector cleanup.

The seven-sample screen rejected it despite correctness:

  • shipping yield path: 130.752 / 266.487 / 548.519 / 1,321.864 ms at 1/2/4/8 lanes;
  • futex peer parking: 146.653 / 318.084 / 560.925 / 1,566.635 ms, exact checksums.

These stop windows are too short for kernel park/wake latency. The next slice should shorten the serialized sweep/publication work that creates the waits; changing the wait primitive is not sufficient.

9 days ago

zig-gc #32 tested batched owned-cell unpublication during sweep. It passed the full dependency suite and focused parallel-GC gate, but the exact seven-pair object_churn A/B regressed 4 lanes by 3.88% and 8 lanes by 1.80%, so both repos were fully reverted and the issue was closed not planned: /zig-utils/zig-gc/issues/32#issuecomment-4988111897

9 days ago

Amortized publication slice is on main and fully documented.

Commits:

  • zig-gc dee13e6 implementation + 0d4c040 README
  • zig-js 76a1119f consumer
  • zig-js c497dc5d geometry/docs correction
  • zig-js db83f9aa saved report, raw TSV, benchmark docs, and README

Validation: zig-gc normal + TSan green; zig-js 777/777 with zero leaks; five focused fixed-shape tests green under TSan; 56/56 raw A/B rows parse with exact checksums. The issue stays open because candidate eight-lane scaling is 0.65x, below the 1.0x acceptance target.

9 days ago

CI run 29648482270 exposed a startup-scheduling hole in the fixed-shape reserve witness: the first active worker stayed on 17-cell refills until a second worker had published, so a slow/staggered runner could exceed the <=160 refill budget even though worker batching was available. I am changing the adaptive condition to batch as soon as one spawned worker exists (the creator-only oracle remains on 17-cell batches), then will verify the focused test repeatedly with the exact CI compiler.

9 days ago

The first-worker fix landed in f5209e4. The previously failing focused witness passed six consecutive runs with the exact CI Zig 0.17.0-dev.1417 compiler, with 0 failures and 0 leaks.

9 days ago

Re-profile of the current binaries ahead of the zig-gc#33 owned-slab enumeration, as requested in the issue body. Profiled 437e6467 (current main) in a clean detached worktree; nothing committed.

Host caveat: Apple M3 Pro on battery (55%, discharging), macOS 27.0, zig 0.17.0-dev.1413, ReleaseFast. Timing deltas below are indicative, not publication-grade.

Timing rows (shared object_churn 100 3 <lanes>, fresh process per lane count, exact checksums matched within and across runs):

  • 1/2/4/8-lane medians: 155.556 / 235.931 / 468.688 / 1,753.870 ms
  • eight-lane scaling from the own one-lane median: 0.71x (3 samples, battery) — still below the 1.0x acceptance target, consistent with the 0.65x candidate result from the amortized-publication A/B.

10-second sample of the exact eight-lane kernel (shared object_churn 100 12 8; 31 threads, 34,955 samples; raw file kept). Inclusive attribution across all worker threads, prior categories mapped to their exact current frame names:

  • scheduler swtch_pri: 19,668 (56.3%) — still the overwhelming dominator; decomposes as 4,104 parked under gc.allocObjectBatch via cthread_yield (allocator slow path yields the thread), 3,090 under joinCooperativeGcRequest via the same yield, 12,474 on other scheduler paths;
  • context.Context.joinCooperativeGcRequest: 4,097 inclusive / 23 self;
  • gc.allocObjectBatch: 11,014 inclusive / 3,196 self;
  • gc.Binding.publishCellAllocationBatch (batch publication): 1,741;
  • context.GcCellBacking.takeFreedSlotFromChunkLocked (freed-slot lookup): 1,538;
  • heap.Heap(gc.Binding).sweepPhaseLocked: 623;
  • heap.Heap(gc.Binding).collectYoung: 568.

Top leaf frames: swtch_pri 19,668 · __ulock_wait2 4,233 · gc.allocObjectBatch 3,196 · publishCellAllocationBatch 1,741 · takeFreedSlotFromChunkLocked 1,538 · vm.tryQuickArrayLoop 854.

Deltas vs the 2026-07-16 profile:

  • NEW: __ulock_wait2 4,233 (12.1%) is the main thread parked in Io.Threaded.futexWait under threadJoinFn while workers run — join-side parking, not worker contention; it was simply unattributed before.
  • All seven prior frames are still present; nothing regressed out of the list.
  • The worker-side hot path is unchanged in shape: allocObjectBatch self work plus its cthread_yield scheduler handoffs, then freed-slot lookup (takeFreedSlotFromChunkLocked) and batch publication (publishCellAllocationBatch) serialization. sweepPhaseLocked/collectYoung remain small.

Read for zig-gc#33: the wait-primitive question stays settled (no retune — the waits are sub-millisecond scheduler handoffs, as the rejected futex prototype showed). The remaining worker-side cost is exactly the allocation/backing enumeration path the owned-slab work targets: batch self-time, freed-slot chunk lookup, and publication, each still taking the allocation-metadata lock per batch. Re-profiling after the zig-gc#33 candidate lands should attribute improvement in allocObjectBatch self + takeFreedSlotFromChunkLocked + publishCellAllocationBatch, not in swtch_pri.

9 days ago

Completed the zig-gc#33 slab-order enumeration experiment and made the acceptance decision from exact-parent evidence.

Landed reusable infrastructure:

  • zig-gc 30ac716: optional owned-cell iterator drives every collector walk and removes Heap.all for opted-in bindings; generic fallback unchanged.
  • zig-js b66fd447: six-way address-ordered merge over exact published slot bitmaps, excluding reservations and holes.
  • normal/TSan backing gates 25/25 each; no-GIL parallel_gc 15/15; exact parallel-build -> quiescent sweep TSan 3/3; zig-gc normal/TSan 42/42.

Rejected production activation after 7 alternating fresh-process pairs per 1/2/4/8 lane:

  • 1: 1.02x
  • 2: 1.00x
  • 4: 1.30x
  • 8: 0.99x; scaling 0.519x -> 0.503x

The binding hook was removed before commit because the eight-lane gate failed. Evidence: 8bdaf18f, docs/.data/object-churn-owned-enumeration-ab-2026-07-18.{md,tsv}. The accepted README benchmark remains unchanged. Next work must combine locality with publication sharding/no heap-wide aggregate lock; this exact traversal-only activation should not be repeated.

9 days ago

Two follow-up contention screens are now preserved in 96b643dd:

  • zig-gc's sharded owned-publication aggregate plus zig-js's exact owned iterator still failed the eight-lane gate (0.94x, 2/7 paired wins), so the binding remained dormant.
  • #310 replaced zig-js's heap-wide pressure-counter RMW with counters under the six existing bucket locks. The isolated candidate regressed 1/2/4/8 lanes to 0.95x / 0.87x / 0.87x / 0.84x and lost every 4/8 pair, so it was reverted.

This rules out the tempting lock-based counter design. The accepted README matrix remains unchanged; the next shared-object-churn change needs new profile evidence and a non-blocking threshold-read design.

9 days ago

Created #325 to bring runtime StringCells and their owned bytes under precise GC liveness/finalization. Besides enabling external-string ABI ownership (#324), this gives string-heavy churn an accounted/reclaimable heap path relevant to the shared-realm scaling work here.

9 days ago

GC string-churn prerequisite landed in 6d2af84 under #325: GC-enabled runtime strings now use collectible StringCells with finalized backing bytes instead of permanent canonical byte storage. #325 remains open for bounded high-churn/no-GIL measurements before this can count as scaling evidence. Full matrix: https://github.com/zig-utils/zig-js/actions/runs/29700993746

9 days ago

Current-source re-profile requested by the prior update (July 20):

  • zig-js e3fdc523 with zig-gc 09f1d51
  • exact bench/comparison.js object_churn, shared mode, 100 jobs/lane, ReleaseFast
  • seven fresh processes per 1/2/4/8-lane row; every checksum was exact
lanesmedianRSDscaling
1110.059 ms2.91%1.00x
2136.379 ms14.56%1.61x
4188.827 ms16.40%2.33x
81,836.050 ms9.27%0.48x

This focused run is not publication evidence and does not replace the accepted July 17 matrix. It confirms that the eight-lane collapse persists on current binaries even though 1/2/4-lane absolute time is substantially better.

A single current mixed GC cell allocation telemetry pass points away from the owned backing lock: at eight workers it recorded 960,048 backing acquisitions with zero contention, 26 arena acquisitions with zero contention, but 8.600 s of native wait time entirely in Thread.join during a 10.071 s no-GIL row (serialized: 2.891 s). Environment/property lock contention was present but sparse relative to acquisitions. The next candidate therefore needs direct cooperative-collection/allocation-phase attribution; the two rejected zig-gc#33 enumeration activations should not be retried.

9 days ago

Opened #426 for the bounded attribution slice. It keeps the exact workload/checksum and README publication gates fixed while exposing cooperative-rendezvous, trace/sweep, allocation, worker, and join timing before another optimization is attempted.

9 days ago

The #426 exact phase profile is published at 8e4580cf: summary, 28 raw samples. At 8 lanes, nursery sweep is 540.893 ms—87.4% of the 618.958 ms cooperative pause—while rendezvous is 0.020 ms and trace 0.399 ms. Every checksum/accounting invariant matched and all seven 8-lane processes collected once with zero timeouts. The next bounded experiment is #427 with zig-utils/zig-gc#42; README scores remain unchanged.

9 days ago

The #427 whole-run backing experiment is closed as a measured negative result. Seven alternating exact-parent pairs per 1/2/4/8 lane kept every checksum/invariant exact, but 8-lane sweep improved only 0.7% while wall time regressed 0.9% (2/7 wins), and 1 lane regressed 1.1%. Both zig-js and zig-gc activations were fully reverted; raw evidence and verdict landed at 91485cb0. README scores remain unchanged.

9 days ago

Completed and published on main.

Key commits:

  • zig-gc a09c015 — batched stable cell identities (supports #445 and this allocation path)
  • 979049f — bounded shared allocation reserve
  • e690a86 — cyclic reclaimed-slot cursor
  • a7cf92c — steady shared benchmark warmup
  • e4187c0 — final report, raw samples, and README publication

Final shared object-churn scaling is 1.26x / 1.16x / 1.20x at 2/4/8 lanes with 6.26% / 11.31% / 2.82% RSD. The full suite passes 1460/1460, focused Debug/TSan backing tests pass 28/28, and the exact-parent A/B records 4.83x faster eight-lane wall time with identical checksums and 1.001x RSS.

This completes the sole remaining child of #49. #44 remains open only for directly comparable independent object churn.

Sign in to comment on this issue.