Skip to content

js: the reads round — flat round_trip 1.21x, derived read 2.01 -> 2.89 M msg/s; 318% -> 262% of generated C++ - #244

Merged
gafferongames merged 5 commits into
mainfrom
js-reads-round
Sep 1, 2026
Merged

js: the reads round — flat round_trip 1.21x, derived read 2.01 -> 2.89 M msg/s; 318% -> 262% of generated C++#244
gafferongames merged 5 commits into
mainfrom
js-reads-round

Conversation

@gafferongames

@gafferongames gafferongames commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

The READS round for generated JavaScript — the follow-on schema#237 named
("read-side window chunking"), plus the lever the fresh profile found under it.
Its runtime twin is mas-bandwidth/serialize.js#11; the two PRs
pair (see "Pairing" below).

Headline (bench/run.sh --only js, the FULL leg — 1 warmup + 7 measured
runs per §2.1, not --quick — before and after in one sitting, bench_mixed,
family gen, codec flat, checks=contract, corpus 6b213fbfa1a03a99, max
rates per §2.2):

path before max after max
round_trip 1,125,162 1,364,364 1.21x
write 2,561,630 2,591,769 1.01x — untouched

Against generated C++ at the same corpus_id: 318.2% → 262.4% on this
sitting's ledger denominator (cpp bench_mixed round_trip max 3,579,797 from
2026-09-01-sitting3), or 312.1% → 257.4% on the LOCKed sitting-2 reference
(3,512,100, untouched by this round). The before leg reproduces the standing
ledger row to 0.02%
(1,125,162 vs 1,124,935). Derived read 2.01 → 2.89 M
msg/s
. Measurement CSVs live outside bench/results/ (scratch, per the
round's rules); ROUND-LOG.md carries their paths and every row verbatim.

1. The instrument first — A/A NULL before any number

A paired instrument times ONE path per invocation over two arms loaded as
separate ES modules, each arm's timing loop built from its own source text so
the two never share a SharedFunctionInfo or a feedback vector; arm order
rotates by round parity; the statistic is the max of 7 rounds. Every pair run
gates first: both arms decode all 64 variants, agree field-for-field, and
re-encode byte-identically — non-equivalent arms are never timed.

path A/A null (B/A on the max) band
read 0.9965, 1.0087, 1.0017 ±0.9%
round_trip 0.9999, 0.9974, 1.0005 ±0.3%
write 0.9955, 0.9947, 0.9876 ±1.3%, biased to arm A

The write path carries a consistent ~0.6% bias to arm A across all three
invocations, so a write claim on this instrument would need ~2%. This round
makes none.
Corroboration that the instrument measures the shipped thing:
its pure-read arm at origin/main prints 2.00 M msg/s — #237's derived-read
figure, from a different harness.

2. MEASURE FIRST — where read time actually was

node --prof and node --cpu-prof over a pure read loop, 4M iterations,
production mode, node 26.7, M2: 78.9% inside ReadBenchMixedFlat itself,
19.0% in V8 runtime calls made from it (BigInt traffic; macOS renders them
as one bogus nearest-symbol, exactly as #237 recorded), GC 3.2–3.8%. Line
ticks put ~30% in the Stats loop (two full window loads per 18-bit element,
80 elements per message) and 14 window loads per entity. The named candidate
is where the time is.

3. The levers

L1 — read-side window chunking. One 64-bit pair load carries exactly 32
valid bits from the cursor, so consecutive fields whose widths sum to ≤ 32 all
extract from the SAME out at literal RELATIVE shifts — static even where the
absolute cursor is dynamic, which is what reaches the loop bodies. The stats
element goes 2 loads → 1, the entity element 14 → 5. Prototyped first as a
transformer over the generated file (ceiling: read 1.29x) before any emitter
work. read 1.3151 / 1.3168 / 1.3152 (null ±0.9%), round_trip 1.1765 /
1.1779 / 1.1808 (null ±0.3%), write 0.9986.

Safety by construction, not inspection: while a window is open the cursor br
LAGS by the bits already served, so pf — the single emission choke point —
settles the window before any text that names br or that opens or closes a
scope. The one deliberate exception is the value-refusal guard, which carries
braces but only tests v/bg and only return falses: it cannot read the
cursor and cannot fall through with the window stale, and it panics if its
condition names br. A second lock: a window never joins across an indent
change.

L2 — wide offset fields decoded in the number domain. A fresh profile of
the post-L1 floor put the largest single line at value.Flux = -2^100n + bg
(11.1%). When a wide field is over 64 bits and its high half is ≤ 53 bits,
that half is an exact Number: the range refusal splits across the halves, the
offset is a numeric add, and exactly one signed 64-bit BigInt comes out of the
scratch. Conditions, all checked in the emitter: bits in (64, 117]; min a
nonzero multiple of 2^64 (so the offset touches only the high half, no
borrow); min's high half and the adjusted high half both inside 2^53. Every
other wide shape keeps the general path — extending it is a named follow-on.
The high word is (nw - nl) / 4294967296 with nl = nw >>> 0, not a rounding
function of nw / 2^32: nw - nl is an exact multiple of 2^32, so no
rounding mode enters the argument at all. read 1.0781 / 1.0766 / 1.0742,
round_trip 1.0444 / 1.0430 / 1.0443.

Refused by measurement

  • Window carry — when a window opens exactly 32 bits after the previous
    one the byte index has advanced by exactly 4, so the previous whi IS the
    new wlo and s2 is unchanged: one move and one load instead of two loads.
    Prototyped, 20 windows carried, wire gate 64/64 byte-identical. 1.0060,
    1.0123, 1.0039 against a ±0.9% null
    — two of three inside it. After L1 the
    read path is not load-bound.
  • Small-value BigInt table — elixir elixir round two: single-match-context reads, cf decode tables, arithmetic float32 rounding (1364% -> ~1301%) #240's decode-table lever applied to
    e0.Damage = BigInt(v): a frozen 256-entry array indexed instead of
    constructed. 0.9718, 0.9709, 0.9706 — measurably SLOWER. V8's
    BigInt(smallNumber) beats a heap-array load of a shared BigInt; the table
    lever does not transfer from the BEAM to V8.
  • BigInt range refusals on their own: ceiling with both bg > Rn
    comparisons removed outright — 1.0083, 1.0060, inside the null. A BigInt
    comparison allocates nothing. Folded into L2 where it is free; never pursued
    alone.
  • 64-bit offset fields in the number domain (WorldTime, raw range 2×10^12,
    well inside 2^53): ceiling with the refusal AND the add removed outright —
    0.9953, 0.9995, inside the null. Only the 128-bit domain pays, which is
    why L2 is gated at bits > 64 and not at "fits in 2^53".

4. Discipline — and one finding about the gates themselves

  • Wire held: nine-language make test green; testdata/wire untouched
    (the re-pin commits touch SOURCE goldens only); both NODE_ENV modes;
    corpus_id 6b213fbfa1a03a99 on every leg.
  • Negative control for L1: with the relative shift off by one bit,
    test/js prints 93 FAILED lines and exits 1. Restored, green, regenerated
    tree byte-identical.
  • The negative control for L2 found a hole in the oracles, and closing it is
    part of the work.
    With the high word taken as Math.trunc(nw / 2^32)
    wrong exactly when the adjusted high half is negative and not a multiple of
    2^32 — test/js stayed green AND the buffer-mutation differential stayed
    green across 189,120 cases.
    Neither can reach that band: BenchMixed's
    header carries a pinned magic constant, so no random or bit-flipped buffer
    reaches a field at all, and the corpus's own Flux values all sit above
    zero. Seeded INSTANCE mutation reaches it — decode a pinned instance,
    perturb ONE leaf across its own domain, re-encode through the writer this
    round does not touch, run both readers. One leaf at a time is the
    load-bearing detail: the checked writer refuses a whole instance for any
    single out-of-contract field, so mutating everything at once produces
    refusals and the deep domains never encode. On that oracle the control goes
    red at once on BenchMixed.Flux, and a direct probe of the band shows 7 of
    11 values decoding wrong.
  • Differential, both oracles, both modes, against origin/main's readers:
    820,160 buffer cases and 152,000 single-leaf instance mutations per mode,
    zero divergences in verdict, fields (bit-exact, so −0 and NaN payloads
    count) and cross read-back.
  • The re-encode oracle needed its own null, and it moved a claim. That leg
    is not deterministic across module instances: an A/A null (origin/main vs a
    byte-identical copy) printed 3 re-encode divergences in one invocation and 0
    in the next, always at float NaN sites — V8 does not promise to preserve a
    NaN's payload as a JS number travels through heap numbers, unboxed double
    fields and DataView stores. The leg now SKIPS NaN-bearing values and says
    how many, and with that exclusion the A/A null is a deterministic zero
    across three consecutive invocations. That is what makes the branch's zero
    mean anything.
  • §2.3 refusal, named not ratioed: the before and emitter-only after legs
    printed the bitpacker write row at 47.7% and 49.3% spread — over §2.3's
    40% INVALID threshold — so no ratio is taken from them, and the 1.16x that
    arithmetic would give is NOT published here.
  • One commit per lever with its paired numbers; every refusal measured on the
    same instrument. ROUND-LOG.md is the round's ledger.
  • Quality gates: gofmt, go vet, golangci-lint (0 issues), modernize, shape
    gate — all clean locally. bench/LOCK untouched: nothing under
    internal/codegen/{c,cpp} or generated/{c,cpp,c-ludicrous} moved.

5. Pairing

The generated flat tier imports nothing, so the two halves do not overlap and
the certification pair shows it: the runtime lever adds nothing to
round_trip (1.2126x emitter-only, 1.2123x paired), and the emitter lands
nothing on family bits — no figure is quoted for that last claim, because
the only rows that could produce one are the two this PR declares INVALID at
47.7%/49.3% spread, and §2.3 is absolute: an invalid row does not become
usable by being cited for a small effect rather than a large one. The claim
rests on the mechanism and on the paired leg's valid bitpacker row. schema's
test/js, test/js-ludicrous (both modes)
and the bench leg all run green against the serialize.js branch through the
§3.5 SERIALIZE_JS override.

6. Follow-ons, named not done

  • The serialize.js pin. schema CI pins SERIALIZE_JS_TAG: v1.1.0. Bumping
    it after BitWriter: single-word 32-bit staging — family bits 1.18x, writeBytes 1.37x serialize.js#11 lands is a separate change; nothing
    in this PR depends on it.
  • Broaden the instance oracle's seed set for RealPacket. The
    magic-header blind spot this round found in BenchMixed is a CLASS, and
    RealWorldFlat is in it: over 400 random buffers the random-buffer oracle
    accepts RealPacket 0 times, exactly as it accepts BenchMixed 0 and
    BenchPacket 0 — a pinned header constant refuses every one before a field
    is read. To be precise about what that does and does not leave uncovered:
    single-leaf instance mutation already reaches RealPacket — it took 611
    encoded mutations at 2 seeds and ~1,500 at the 5 this round ran, and it is
    under the same zero-divergence result as everything else. What is thin is
    its SEED SET: only 1 of the 24 pinned buffers decodes as a RealPacket, so
    the whole mutation walk starts from a single point, where BenchMixed gets
    2 and the shallow shapes get 23. Widening that seed set (or pinning a second
    RealPacket instance) is the actual work, and it is worth doing for the
    class rather than for this shape.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

All contributors have signed the CAA. Thank you.
Posted by the CLA Assistant Lite bot.

rowan-claude and others added 5 commits September 2, 2026 02:10
The flat tier's read side reloaded a 64-bit window per field. One window
carries exactly 32 valid bits from the cursor, so consecutive fields whose
widths sum to 32 or less all extract from the SAME `out` at literal RELATIVE
shifts — static even where the absolute cursor is dynamic, which is what
reaches the loop bodies. On bench_mixed the stats element goes 2 loads to 1
and the entity element 14 to 5.

While a window is open the cursor lags by the bits already served, so pf —
the single emission choke point — settles it before any text that names br or
that opens or closes a scope. The one exception is the value-refusal guard,
which carries braces but only tests v/bg and only returns false: it cannot
read the cursor and cannot fall through with the window stale, and it panics
if its condition names br. A window also never joins across an indent change.

Paired instrument, single-path runs, arm rotation, max of 7 rounds, three
invocations each: read 1.3151/1.3168/1.3152 against a +/-0.9% A/A null,
round_trip 1.1765/1.1779/1.1808 against +/-0.3%, write 0.9986 (untouched).

Negative control: with the relative shift off by one bit, test/js prints 93
FAILED lines and exits 1. Differential against origin/main's readers over the
pinned corpus, its mutations and random buffers: 820,160 cases per mode,
zero divergences in verdict, fields and cross read-back, both NODE_ENV modes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A fresh profile of the post-chunking floor put the largest single line at
`value.Flux = -2^100n + bg` (11.1% of read ticks), with the two 128-bit
assemblies at 7.1%. A 128-bit-domain BigInt op is multi-digit and allocating,
and the shape cost a second getBigUint64, a shift by 64, an or, a BigInt
comparison and a BigInt add.

When a wide field is over 64 bits and its high half is at most 53 bits, that
half is an exact Number. The range refusal then splits across the halves —
numeric on the high, the low comparison only on the exact boundary — the
offset is a numeric add, and exactly one signed 64-bit BigInt comes out of
the scratch. Conditions, all checked in the emitter: bits in (64, 117]; min a
nonzero multiple of 2^64, so the offset touches only the high half and the
low 64 bits pass through with no borrow; min's high half and the adjusted
high half both inside 2^53. Every other wide shape keeps the general path.

The high word is `(nw - nl) / 4294967296` with `nl = nw >>> 0`, not a
rounding function of nw / 2^32: `nw - nl` is an exact multiple of 2^32, so
the division is exact and no rounding mode enters the argument.

read 1.0781 / 1.0766 / 1.0742 against a +/-0.9% null, round_trip 1.0444 /
1.0430 / 1.0443 against +/-0.3%. Cumulative for the branch against
origin/main: read 1.4199 / 1.4175 / 1.4185, round_trip 1.2250 / 1.2286 /
1.2305.

The negative control found a hole in the oracles. With the high word taken as
Math.trunc(nw / 2^32) — wrong exactly when the adjusted high half is negative
and not a multiple of 2^32 — test/js stayed green AND the buffer-mutation
differential stayed green across 189,120 cases: BenchMixed's header carries a
pinned magic constant, so no random or bit-flipped buffer ever reaches a
field, and the corpus's own Flux values all sit above zero. Seeded INSTANCE
mutation reaches it: perturb one leaf of a decoded instance across its own
domain, re-encode through the untouched writer, run both readers. One leaf at
a time is load-bearing — the checked writer refuses a whole instance for any
single out-of-contract field. On that oracle the trunc control goes red at
once on BenchMixed.Flux, and a direct probe of the band shows 7 of 11 values
decoding wrong. Restored: green.

Both oracles, both NODE_ENV modes, against origin/main's readers: 152,000
single-leaf instance mutations and 263,360 buffer cases per mode, zero
divergences. Nine-leg make test green; testdata/wire untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…l named

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The attribution paragraph quoted 1.0248x for "the emitter lands nothing on
family bits" — computed from the two bitpacker write rows the same document
declares INVALID at 47.7% and 49.3% spread. §2.3 is absolute: an invalid row
does not become usable by being cited for a small effect rather than a large
one. The figure is gone; the claim rests on the mechanism (the generated flat
tier imports nothing) and on the paired leg's own valid bitpacker row.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gafferongames
gafferongames marked this pull request as ready for review September 1, 2026 16:12
@gafferongames

Copy link
Copy Markdown
Contributor Author

Adversarially reviewed as a pair with serialize.js#11: mechanical audit of all 852 window extractions across 114 generated read functions (zero violations), the brace-lock proven load-bearing by sabotage, the L2 number-domain boundary probed at every edge with the oracle asymmetry reproduced exactly (buffer oracle blind, instance oracle red — the round's own finding), an independent 160k-case pair differential at zero divergence with all 240 write functions proven structurally untouched, and every number recomputed to four decimals including the published A/A nulls. Verdict: MERGE.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants