ReviewOS

zig-utils/zig-js

JIT: keep dense JSON loops resident after direct-access guard misses

#1017
Closed glennmichael123 opened this 9 days ago ·
9 days ago

Parent: #473 Follows: #1014, #1015, #1016

Measured problem

At 0efe89ed, the clean real-GC ReleaseFast frozen representative_json row publishes two optimizer artifacts and preserves checksum 324952086, but one scored invocation records 243,515 optimizer OSR entries and 243,515 deoptimizations. Forced VM records 23,184,279 dispatches; JIT reduces that to 13,277,849, yet the first paired sample regresses from 357.884 ms to 386.234 ms because the region repeatedly side-exits and re-enters.

The rate is approximately one side exit per inner JSON iteration, not one normal loop exit. Existing packed-index tests assert semantic output and zero runtime callbacks, but do not bound native side exits, so a direct numeric guard miss can pass all current tests while destroying residency.

Scope

  • Pin the churn with a focused dense read/write loop that asserts exact output, steps, direct-path callbacks, OSR entries, and deoptimization count.
  • Identify the exact guard/deopt point rather than widening eligibility speculatively.
  • Keep the native region resident across the safe common case, preserving prototype/indexed-property semantics, moving-GC roots, invalidation, step budgets, and exact side-exit reconstruction.
  • Re-run the frozen JSON base/variant and anti-specialization controls with JIT on/off and exact-parent binaries.

Acceptance

  • The focused loop performs at most one normal loop-exit deoptimization per invocation and never hides an interpreter callback.
  • representative_json checksum remains 324952086, optimizer residency is stable, and JIT no longer regresses versus forced VM.
  • ReleaseFast, TSan, real-GC focused tests, required-VM agreement, JSON test262, OOM/deep-input, and clean seven-sample benchmark evidence pass.

Guardrails

No threshold-only suppression, source/workload recognition, disabled JIT, omitted work, changed checksum, relaxed indexed/prototype semantics, or benchmark-only special case.

9 days ago

Partial fix landed directly on main as ac67db5 (perf(jit): inline primitive string length in OSR).

Measured diagnosis: the dominant numeric get_prop side exit was encoded.length. The optimizer now reads the immutable cached UTF-16 length from StringCell with an acquire load, preserves the existing dense-array length path, and side-exits the exact-length sentinel to canonical runtime semantics.

Verification:

  • Focused primitive-string + dense-array length test: ReleaseFast pass; ReleaseSafe+TSan pass; forced unknown-length sentinel exits at the original get_prop.
  • AArch64 acquire-load encoding test passes.
  • Full focused JIT sweep: 449 pass, 1 skip, 1 fail. The single failure is the unrelated optimizer call-link writer/writer/reset concurrency test and reproduces in isolation; all optimizer/backend tests around this change passed.
  • Frozen representative_json checksum: 324952086 before and after.
  • Invocation OSR entries/deoptimizations: 243,515 -> 17,113 (14.2x fewer). A temporary trace showed every sampled residual exit at loop-header load_local offsets 64/122 in the regular checkpoint pattern, not a property/index guard.
  • Exact-parent interleaved 7-sample median: 400.774 ms -> 364.558 ms (9.0% lower).
  • Same-binary JIT-on/off interleaved 7-sample median: 365.050 ms vs 357.070 ms. JIT remains 2.2% slower, so this issue stays open and its no-regression acceptance box remains unchecked.
9 days ago

Progress from child #1018 / 8ca0d15: the frozen checksum remains 324952086, and the same-binary seven-sample median is now 350.892 ms with JIT versus 351.799 ms forced-VM. This clears the measured JIT-regression symptom, but #1017 remains open until the full acceptance matrix and focused residency assertion are complete.

9 days ago

Formal exact-revision evidence collection has started for 0efe89ed -> ac67db51. The accepted base artifact preserves checksum 324952086 and records a stable retired-instruction median reduction 5,967,149,416 -> 5,565,259,486 (0.93265x). Wall time is diagnostic-only because candidate RSD was noisy; remaining rows will run after an unrelated Home test releases CPU.

9 days ago

Completed on main with the production real-GC path and exact evidence.

Landed:

  • ac67db5: inline primitive String length in optimizer regions
  • fe4b66e: seven-pair exact-parent base/variant/control evidence
  • 8c2a68b + f679e89: same-binary JIT versus required-VM residency mode and 28 raw samples
  • 0d2c6cf: consolidated dated report and README/docs summary

Measured result:

  • representative_json retired instructions: 0.9326496x parent
  • representative_json_variant: 0.9328213x parent
  • four controls: 0.9994749x to 1.0003176x
  • final-binary JIT/required-VM elapsed: 0.990x / 0.992x with exact checksums (diagnostic; base VM RSD 11.25%)

Qualification: JSON test262 165/165 in tree and required-VM modes, direct-root real-GC JSON/OOM 10/10, deep-stringify 2/2, and catchable RangeError at 50,000 levels in both modes. The larger remaining traversal gap is separated into #1019.

Sign in to comment on this issue.