ReviewOS

zig-utils/zig-js

Perf: quicken fixed-shape object allocation and dense replacement loops

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

Parent: #62 Related: #47, #52, #56, #70

Problem

At main 971f994, a fresh seven-sample direct object_churn run measures 652.949 ms for zig-js versus 126.055 ms for system JavaScriptCore at 100 equal jobs and checksum 1030552128, leaving a 5.18x throughput gap. A valid ten-sample symbolized profile attributes 1,146 top-of-stack samples to vm.runChunk, versus 93 to nursery sweep and 48 to collectYoung.

The hot counted loop still dispatches every bytecode for dense indexed selection, own-data reads, numeric calculation, fresh fixed-name object literal construction, dense replacement, checksum update, and counter update. JSC compiles this loop. The next fair step is a guarded structural quickening tier that continues performing every real allocation, initialization, write barrier, and collection checkpoint while removing repeated bytecode dispatch.

Profile: /tmp/zig-js-71-profile.sample.txt (local evidence; ten completed samples, exact checksum).

Scope

  • recognize the immutable opcode/data-flow shape of a counted loop that selects a present dense element, reads an own numeric property, creates a fixed-shape ordinary object, replaces the element, and updates numeric locals;
  • compile arbitrary local indexes, constants, fixed property names, masks, bounds, and shape/IC sites from bytecode only—never inspect source text, benchmark/function names, or host configuration;
  • require an isolated frame, an ordinary dense array with present elements, plain own-data displaced objects, warmed literal transitions, numeric operands, and exact shape/slot guards;
  • execute real newObject allocations, literal transitions, GC barriers, dense replacement, and logical checkpoint/step accounting;
  • fall back before externally visible mutation when any guard fails;
  • leave shared/no-GIL execution on ordinary bytecode until a separately race-safe kernel is proven;
  • measure a clean pinned A/B and retain only a material exact-checksum improvement.

Acceptance criteria

  • The plan compiler is structural and accepts renamed locals/properties/constants with the same supported data flow.
  • Proxy, accessor, descriptor, hole, prototype, non-number, and shape misses preserve ordinary semantics.
  • Every successful iteration performs a real GC-managed object allocation and insertion barrier.
  • Logical bytecode steps and 1024-step GC/checkpoint boundaries remain exact.
  • Focused quickening/fallback/GC tests pass and prove the optimized path is reached.
  • A clean order-balanced A/B improves object_churn with checksum 1030552128 on every sample.
  • Full unit and benchmark-harness validation pass before push.
  • The retained conventional commit references this issue and #62 receives the evidence.
9 days ago

Implemented and pushed to main in c4f4682.

What landed:

  • source/name-independent bytecode and data-flow recognition for the fixed-shape allocation loop
  • real object allocation, literal transitions, dense replacement, insertion barriers, and GC participation
  • exact interpreter step accounting and checkpoint chunking
  • isolated-frame-only execution; synchronized/no-GIL execution stays on ordinary bytecode
  • guarded fallback for proxies, accessors, attributes, holes, indexed-name overrides, unexpected shapes, and non-numeric inputs
  • focused semantic coverage compares optimized and ordinary execution result bits plus exact steps, and verifies Proxy/accessor fallback

Validation:

  • full unit suite: 755/755 passed
  • benchmark harness: 7/7 passed
  • post-review focused test: 2/2 passed
  • git diff --check passed

Pinned 12-process-per-side A/B, alternating order, exact checksum 1030552128 in all 24 runs:

  • baseline 971f994 median: 578.091563 ms
  • c4f4682 candidate median: 266.911521 ms
  • elapsed reduction: 53.83%
  • throughput improvement: about 2.166x

Fresh 7-process-per-engine comparison, alternating order and exact checksum in all 14 runs:

  • zig-js median: 296.352 ms
  • JavaScriptCore median: 128.644 ms
  • remaining JSC gap: 2.303x

This completes the scoped general quickening optimization. The broader object_churn goal remains open in #62 because this checkpoint does not yet beat JSC.

Sign in to comment on this issue.