ReviewOS

zig-utils/zig-js

Intl.DurationFormat: publish complete immutable resolved state

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

Parent: #475 Benchmark matrix: #460

Problem

Intl.DurationFormat eagerly observes and normalizes its options, but publishes the result as a hidden \0opts JavaScript object. Every format, formatToParts, and resolvedOptions call then performs synchronized property lookups and reruns the ten-unit style/display cascade.

The existing 100-job representative_intl_duration_format_parts attribution row has checksum 58800326 and records 10,553,267 property-lock acquisitions, 6,080,927 backing allocations, 3,320,067 GC-cell allocations, and 120 collections across configuration, warmup, and invocation. A dedicated resolved-state row is still needed so state publication/reflection is distinguishable from number and list formatting.

Scope

  • Freeze steady formatToParts and repeated resolvedOptions consumers with checksums shared by zig-js and system JSC.
  • Preserve the complete observable InitializeDurationFormat option order: localeMatcher, numberingSystem, style, each unit style/display pair, then fractionalDigits.
  • Publish one object-owned, GC-finalized immutable record containing the owned resolved locale/numbering system, base style, all ten resolved unit styles/displays, and optional fractionalDigits.
  • Remove the hidden normalized-options object. Require the native record as the receiver brand for format, formatToParts, and resolvedOptions.
  • Read the record directly in hot formatting and reflection paths; closed enum reflection must not allocate per-instance category strings.
  • Keep temporary NumberFormat and output storage invocation-local and bounded.

Acceptance

  • Invalid receivers cannot forge the DurationFormat brand with NUL-led properties.
  • Throwing/proxy option getters prove exact observation order and no repeated reads.
  • Constructor OOM rolls back every partially allocated record/string; teardown and moving GC finalize exactly once.
  • Bounded repeated reflection/formatting, moving nursery, parallel shared-realm/no-GIL, and TSan probes pass.
  • DurationFormat test262 and full Intl402 do not regress; focused unit and full sharded unit suites are clean.
  • Exact-parent attribution reports checksum, property-lock, backing-allocation/bytes, GC-cell, collection, and retained-memory deltas for both frozen rows.
  • Retain only changes with causal improvement and no unbounded cache or locale/option workaround.
9 days ago

Benchmark boundary landed in 406361e3.

The new 6,400-call resolved-state row covers all 24 reflected fields and has identical zig-js/system-JSC checksum 742401512. The 100-job exact baseline records 8,687,981 property locks, 5,076,305 backing allocations, 712,638,818 allocated backing bytes, 232,383,999 current / 232,560,603 peak backing bytes, 2,929,756 GC cells, 93 collections, and 183,238,656 retained RSS bytes. The pre-existing parts row remains unchanged with shared checksum 58800326.

9 days ago

Implemented and landed on main in perf(intl): publish DurationFormat state (#588), measured against the frozen-row parent 406361e3.

The hidden normalized-options object is gone. Each formatter now owns a rollback-safe, GC-finalized native record containing the resolved locale/numbering system, base style, all ten unit style/display pairs, and fractional digits. format, formatToParts, and resolvedOptions require that native brand and consume the record directly. Closed enum reflection uses static values.

Correctness coverage now proves all 24 observable option reads, conflict deferral until the final getter, explicit-style display defaults, rejection of forged NUL-led properties, constructor OOM rollback, moving/finalizing GC, shared-realm concurrent reads, and non-default numbering output/parts (arab digits and decimal symbol). The candidate corpus initially exposed an explicit-style display-default regression at 109/110; the rule was corrected and promoted to a focused assertion before landing.

Exact-parent attribution:

  • formatToParts, checksum 58800326: property locks 10,553,606 -> 10,233,383 (-3.03%); backing allocations 6,081,164 -> 6,081,150 (-14); GC cells 3,320,202 -> 3,320,170 (-32); collections remain 120. Allocated backing rises 219 bytes, current/peak backing rise 2,645 bytes, and retained RSS falls 32,768 bytes. The order-balanced six-sample median is flat: 1,171,914,959 -> 1,172,068,042 ns (+0.013%).
  • resolvedOptions, checksum 742401512: property locks 8,687,981 -> 8,316,558 (-4.28%); backing allocations 5,076,305 -> 4,807,491 (-268,814 / -5.30%); allocated backing 712,638,818 -> 711,342,141 (-1,296,677 bytes); GC cells 2,929,756 -> 2,660,924 (-268,832 / -9.18%); collections 93 -> 89. Current backing falls 333,935 bytes; peak/RSS rise a bounded 206,807/245,760 bytes. The six-sample median improves 1,040,478,521 -> 1,023,961,688 ns (-1.59%).

Validation:

  • DurationFormat test262: 110 -> 110, 0 failures.
  • Full Intl402: 3,341 passing, 0 failures.
  • Focused normal and TSan tests: 3/3.
  • Bounded precise-heap, moving-nursery, shared-realm, and ownership tests: 4/4.
  • Full sharded unit gate: 1,698 passed, 1 skipped, 0 failed, 0 leaked.

All acceptance conditions are satisfied without a cache or locale/option workaround.

Sign in to comment on this issue.