Skip to content

dart: the laggard round — chunked lanes, windowed reads, the #165 bulk-bytes path; 363% -> 221% of generated C++ - #238

Merged
gafferongames merged 7 commits into
mainfrom
dart-round
Sep 1, 2026
Merged

dart: the laggard round — chunked lanes, windowed reads, the #165 bulk-bytes path; 363% -> 221% of generated C++#238
gafferongames merged 7 commits into
mainfrom
dart-round

Conversation

@gafferongames

Copy link
Copy Markdown
Contributor

Laggard round for generated Dart (#165, the last open laggard round). The issue's named lead — the bulk-bytes path — lands, and the profile named three larger levers beside it.

Headline (--only dart --quick, both halves in one sitting minutes apart, bench_mixed, family gen, AOT, checks=contract, corpus 6b213fbfa1a03a99, max rates per §2.2, spreads 0.07–2.28%):

path before max after max
write 1,900,191 2,857,586 1.50x
round_trip 972,818 1,591,336 1.64x

Against the arm64 sitting-2 certification reference at the same corpus_id (cpp round_trip max 3,512,100 — the LOCKed reference, untouched by this round): 361% → 220.7% of generated C++ on the canonical round_trip blend — the ledger's standing 363% reproduced within 0.6% by this pair's own before leg. Derived read moved 1.99 → 3.60 M msg/s. The measurement CSVs live outside bench/results/ (scratch, per the round's rules); the numbers above are their rows verbatim.

1. MEASURE FIRST — where the time actually was

Dart's --observe profiler is interactive, so the round profiled the AOT executable with macOS sample (file output, no UI) plus AOT micro-harnesses per the family method:

  • write phase: ~80% of user-code samples inside writeBenchMixed itself — the per-field merge with its data-dependent flush branch; ~13% in one ~72-byte out-of-line stub reached from the driver loop (the generic tear-off dispatch of the data-driven contract — harness parity, every leg's driver calls through a function value; named, not touched).
  • round_trip phase: reader region 47%, re-encode writer 40%, same stub 6%.
  • The reader reloaded its 64-bit window — one getUint64 plus a tail branch — at EVERY field.

The micro that ranked the forms (interleaved arms, AOT, M2, stats-loop shape): chunked write merge 1.21x over per-field; chunked window read 1.35x over per-field reload; bulk 4-byte grouping 2.33x over per-byte; Uint64List store vs ByteData.setUint64 +0.8% (see refusals).

2. THE LEVERS, each landed as one commit with its paired numbers

# lever leg (write / round_trip, M msg/s)
L1 Chunked write merges, 64-bit lanes — the family lever (#198/#208/#237), capped at Dart's native 64-bit int where the js number domain held it to 32; wide 33..64-bit fields merge ONCE (their wire bits are contiguous, so the single merge emits the identical bytes the two-group form did) 1.894→2.380 (+26%) / 0.974→1.088 (+12%)
L2 Bulk-bytes write path — the #165 lead: string/bytes bodies and bare [N]uint8 arrays stop paying a merge per byte; ≤8-byte static runs queue as chunk pieces (joining neighbor fields in one merge), longer/dynamic runs take 4 bytes per merge with a byte tail. No alignment requirement — LSB-first merging of the concatenation equals merging the bytes in sequence at any bit position, so it reaches the unaligned loadout too 2.380→2.499 (+5.0%) / 1.088→1.104 (+1.4%) — bulk is only 3.65% of bench_mixed's wire by construction; the lever is for the real packets the issue names
L3 Chunked read windows: one load + one tail branch per run, not per field — the load folds the byte-interior shift in and guarantees 57 valid bits (in the tail arm, every bounds-proven remaining bit), consecutive fields extract at literal relative shifts through a compile-time ledger; wide 33..57-bit fields extract in one masked read; short bare byte arrays unroll into the same windows (#165's read twin) 2.499→2.493 (flat) / 1.104→1.367 (+23.8%)
L4 Grouped counted loops: k = lane/elemBits elements per iteration (64-bit chunk lane on write, 57-bit window on read) with a remainder loop — the chunk accumulator and window ledger span the unrolled elements, so bench_mixed's 80-element stat array drops from one merge and one window load per element to one per three. Bounded FIRST by a hand-patched generated leg (+10% round_trip read-side alone), then landed 2.493→2.856 (+14.6%) / 1.367→1.592 (+16.5%)

Refused by measurement (results, per the method)

  • Uint64List word stores instead of ByteData.setUint64: micro 3,888 → 3,918 K ops/s (+0.8%) — noise-level; the generated API keeps its ByteData surface. Refused.
  • Temp-based chunking of statement-form merges (compressed floats): the shape carries 3 such fields against ~120 remaining merges; js: the write round — flat writes 1.89x, round_trip 1.47x; 461% -> 313% of generated C++ #237 bounded the identical lever at 1–2% and refused — same here, bounded under 1% by shape arithmetic. Not taken.
  • Slack-window reads (drop the tail branch by requiring 8 spare buffer bytes): refused on contract, not measurement — the Dart reader's no-slack stance is the family read contract.
  • The remaining 13–15% driver-dispatch cost: the data-driven contract's generic function-value call, present in every leg's driver — harness parity, named and left alone.

3. Discipline

  • Wire held: full nine-language make test green; testdata/wire untouched (the one re-pin commit touches SOURCE goldens only — SPEC §3.1's deliberate-change path); the bench leg's §1.5 golden + 64-variant decode→re-encode gates pass at corpus_id 6b213fbfa1a03a99.
  • Byte-equivalence argument, stated: an LSB-first packer merging a concatenation of masked pieces emits the identical byte stream to merging the pieces in sequence, and a contiguous 33..64-bit field's single merge equals its two 32-bit groups low-dword-first; the gates then prove it on every pinned instance and variant.
  • Differential oracle, run rather than assumed (the significant-restructure rule, as in js: the write round — flat writes 1.89x, round_trip 1.47x; 461% -> 313% of generated C++ #237's review): one harness compiled against the origin/main tree and the branch tree, 5 seeds × 2,000 rounds × 3 corpora (bench_mixed's 64 variants, real_packet, ludicrous_state ×2 goldens) of bit-flipped, numBits-jittered, view-length-varied hostile buffers — accept/reject verdicts and every accepted re-encoding folded and byte-identical (~14k accepts across 30k rounds, both paths exercised).
  • Negative control: one relative shift in a generated chunk broken by one bit → the oracle's fold diverges; restored → identical again.
  • Ledger correctness designed out, not tested away: the window ledger and chunk accumulator invalidate/flush at every scope boundary and every dynamic bitsRead/cursor move; a piece referencing a block-scoped temp flushes inside its scope; grouped-loop bodies begin with an invalidated ledger so every iteration executes the same straight-line code.
  • Quality gates: gofmt clean, go vet clean, modernize clean, dart analyze + dart format --set-exit-if-changed clean on all generated dart trees, shape-gate clean (19 exemptions, all on ledger), generated tree current.
  • bench/LOCK untouched: nothing under internal/codegen/{c,cpp} or generated/{c,cpp,c-ludicrous} moved. The dart emitter's tests (fixed-default, reserved-name pins) stay green.
  • One commit per lever, paired numbers per commit; refusals measured on the same instrument.

Named follow-on (land-and-expand, not this round)

  • Grouped FIXED arrays of static-width elements — the L4 unroll currently reaches counted arrays only; the fixed-array twin is mechanical when a shape warrants it.
  • Bulk read for long byte bodies (4-byte getUint32 loads feeding the byte stores) — bounded small on this corpus; priced when a bulk-heavy real schema needs it.

Closes #165.

🤖 Generated with Claude Code

gafferongames and others added 7 commits September 1, 2026 13:39
Adjacent constant-width pure-expression fields pack into one staged word
with literal relative shifts (the family lever, #198/#208/#237), capped at
Dart's native 64-bit lane where the js number domain held it to 32. Wide
33..64-bit fields merge once instead of twice — their wire bits are
contiguous, so the bytes are identical by the concatenation argument.

Leg (quick, arm64 M2): write 1.894 -> 2.380 M msg/s (1.26x),
round_trip 0.974 -> 1.088 (1.12x). Golden gate + 64-variant re-encode
green; dart conformance suites green with asserts on.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
String/bytes bodies and bare [N]uint8 arrays stop paying one merge per
byte: static runs of 8 or fewer bytes queue as chunk pieces (joining
neighboring fields in one merge), longer and dynamic runs take 4 bytes
per merge with a byte tail. No alignment requirement: LSB-first merging
of the concatenation equals merging the bytes in sequence at any bit
position, so the fix reaches the unaligned loadout too.

Micro (AOT, M2): 4-byte grouping 2.33x, per-byte baseline 1.00x.
Leg (quick): write 2.380 -> 2.499 M msg/s (+5.0%), round_trip
1.088 -> 1.104 (+1.4%) — bulk is only 3.65% of bench_mixed's wire by
construction; the lever is for the real packets the issue names.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t per field

The reader kept reloading its 64-bit window at every field. A load now
folds the byte-interior shift in and guarantees 57 valid bits (in the
tail arm, every bounds-proven remaining bit), so consecutive
constant-width fields extract at literal relative shifts from one
window through a compile-time ledger. Wide 33..57-bit fields extract in
one masked read — their groups are contiguous on the wire. Short bare
byte arrays unroll into the same windows (#165's read twin). The ledger
invalidates at every dynamic bitsRead move and scope boundary.

Micro (AOT, M2): chunked window 1.35x over per-field reload.
Leg (quick): round_trip 1.104 -> 1.367 M msg/s (+23.8%), write flat;
derived read 2.01 -> 3.03 M msg/s.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A counted array of static-width elements unrolls k = lane/elemBits
elements per iteration (64-bit chunk lane on the write side, 57-bit
window on the read side), with a remainder loop. The chunk accumulator
and window ledger span the unrolled elements, so short elements merge
together and extract from one load — bench_mixed's 80-element stat
array drops from one merge and one window load per element to one per
three. Hoisted element refs get unique names (e0, e0g1, e0g2) so the
unroll shares one scope; correctness rides the same ledger/flush
machinery as straight-line code.

Bounded first by a hand-patched generated leg (+10% round_trip on the
read side alone). Leg (quick): write 2.493 -> 2.856 M msg/s (+14.6%),
round_trip 1.367 -> 1.592 (+16.5%).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wire goldens untouched — testdata/wire byte pins hold in every leg; the
re-pin is the SOURCE form only (SPEC §3.1's deliberate-change path).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
before 1,900,191/972,818 (reproducing the standing ledger row within
0.6%), after 2,857,586/1,591,336; spreads 0.07-2.28%; one sitting,
corpus 6b213fbfa1a03a99.

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

Copy link
Copy Markdown
Contributor Author

Adversarially reviewed: the three proof obligations audited and attacked (the 57-bit read-window bound is tight — worst generated extraction is exactly 57, zero violations across 815 window extractions in four corpora; the tail arm is bounds-covered exactly; the 64-shift edge is defined-zero in Dart at precisely the full-word case), plus an independent instance-space differential on the opposite axis from the round's own buffer fuzz (dart:mirrors mutation, subnormals and NaN/Inf through compressed floats, both assert modes, cross read-back — zero divergence over nine units × seven seeds). Numbers recompute exactly from the committed pair. 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.

Dart backend: bulk-bytes path for aligned byte arrays — the named residue of the refuted packet-write prediction row

2 participants