ReviewOS

zig-utils/zig-js

Perf: beat JSC on cold context creation and source startup

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

Problem

The symmetric comparison now wins its warmed execution rows overall, but the cold lifecycle remains the largest verified gap. Each cold sample includes OS-thread spawn, context creation, global/intrinsic installation, workload parse/compile/configuration, one exact invocation, context destruction, and join for both engines.

The 2026-07-13 quick matrix shows zig-js roughly 3x to 8x slower than JSC on most cold rows. This is real product cost and must not be removed from the benchmark boundary.

Work

  • Profile one-lane cold context creation separately from parse/compile and first invocation.
  • Cache or share immutable intrinsic metadata and shapes where realm semantics permit.
  • Reduce allocator, global installation, parser, compiler, and teardown overhead.
  • Preserve independent realm identity and creator-thread affinity.
  • Keep the symmetric cold runner and exact source unchanged.
  • Add focused startup counters or microbenchmarks only where they identify actionable cost.

Acceptance

  • Every full cold row remains above the benchmark timing floor and checksum-valid.
  • zig-js beats the matching JSC cold lifecycle at 1, 2, 4, and 8 lanes for all four comparison workloads.
  • No warmed or shared regression.
  • Full tests and the complete publication matrix pass.
  • The dated report and README describe the cold boundary and resulting ratios.
9 days ago

First accepted full-matrix evidence at c1bd949 refines the cold target:

  • arithmetic: zig-js is 3.03x to 3.39x slower than JSC across 1 to 8 lanes
  • properties: 1.27x to 1.71x slower
  • arrays: wins at 1 to 4 lanes, 1.02x slower at 8 lanes
  • Fibonacci: wins by 3.0x to 4.2x

Cold arithmetic is the dominant startup gap: 822.043 ms at one lane vs JSC 242.576 ms. The full report passed timing/checksum validation but remains uncommitted until the losing rows are fixed and rerun.

9 days ago

Landed 1c720b36 (perf(jit): tier numeric candidates on first call (#59)). The baseline compiler now exposes an allocation-free opcode/size candidate filter. Numeric candidates tier on their first invocation (the only invocation in cold mode), while object/property/call-heavy chunks retain the three-entry threshold and avoid known-useless compiler setup.

Focused native-tier tests pass with exact step/checkpoint and non-number fallback behavior. Five-sample evidence at full work:

  • cold arithmetic 1 lane: ~809 ms parent -> ~77.5 ms; JSC ~237.5 ms
  • cold arithmetic 8 lanes: ~174.4 ms; JSC ~363.4 ms
  • warmed single arithmetic: neutral at ~55.3 ms
  • cold properties 1 lane: neutral versus parent (~42 ms), confirming the filter removes the eager-all regression

Current targeted cold endpoint matrix now has zig-js winning arithmetic, arrays, and Fibonacci at 1 lane; arithmetic and Fibonacci at 8 lanes; and properties at 1 lane. Remaining losses are concurrent cold properties (~153 vs ~126 ms) and arrays (~236 vs ~210 ms), now isolated to multi-context startup/contention rather than first-call numeric execution.

9 days ago

Landed the allocator-side cold lifecycle correction in eb449b6d (bench: use production allocator for independent contexts (#59)). Independent workers now pass the process-wide thread-safe libc allocator to zig-js contexts, matching a representative production embedder and JSCs use of a cached process allocator. The prior page allocator translated arena/GC backing churn directly into mmap/munmap; the profile was dominated by those syscalls. The exact allocator scope and cold timed boundary are now documented in docs/benchmarks.md.

Paired 7-sample parent/current medians at full work:

  • properties 1 lane: ~44.9 -> ~24.0 ms
  • properties 8 lanes: ~134.0 -> ~35.8 ms
  • arrays 1 lane: ~91.3 -> ~62.4 ms
  • arrays 8 lanes: ~228.7 -> ~102.3 ms

Recent same-machine JSC medians were ~80.0/~125.9 ms for cold properties and ~126.8/~210.3 ms for cold arrays (1/8 lanes), so these formerly losing endpoints now win by large margins. The benchmark contract suite remains 7/7. Keeping #59 open until the full clean matrix confirms every lane count and the final report is published.

9 days ago

Completed. All 32 full cold rows pass the timing/checksum contract, and zig-js beats JSC for all four workloads at lanes 1/2/4/8. Key commits: eager candidate tiering 1c720b36, documented production allocator eb449b6d; accepted evidence ccee8842, README/docs 09d543c3.

Sign in to comment on this issue.