From 40e7f24e996dd5e4f30777b13d9fb89b73e56029 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 06:20:49 +0000 Subject: [PATCH 1/2] board: record R6-R8 and the twice-repeated measurement-ledger defect PR #24 merged the R4-R8 arc but carried a board entry for R4/R5 only. This discharges the rest, and records the defect that arc produced twice. R7 shipped prose quoting a throughput range its own pinned runs contradicted. It was repaired -- and R8 committed the identical defect one commit later. Root cause is mechanical: prose hand-copied from run N while the raw block was regenerated at run N+1, which makes every such artifact one regeneration away from lying about itself. Repaired structurally by generating the report from the runs it just captured. That turned out to be the right shape rather than merely a safer one: the first regeneration moved B' ~25% while every structural conclusion held identically. The stability of conclusions under unstable absolutes is itself the result, and only a regenerable artifact can show it. Also records the two other review findings (toolchain not unified across the control and the arm; sweep-only comparison understating the lawful mask shape, which wins END-TO-END once population construction is counted) and the R6-R8 measured architecture. --- .claude/board/EPIPHANIES.md | 78 +++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index 3be79d8..774842f 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -4,6 +4,84 @@ > `**Status:**`/`**Confidence:**` line. A correction gets its own new, > dated entry that references the one it corrects — the storno rule. +## 2026-08-25 — E-LGJ-THE-MEASUREMENT-LEDGER-DRIFTED-TWICE-SO-THE-REPORT-IS-NOW-GENERATED-1 + +**Status:** DEFECT FOUND TWICE, REPAIRED STRUCTURALLY (R7, R8; merged in PR #24). +**Confidence:** The defect and its repair are certain. The measurements are ranges, +regenerable, and deliberately not pinned as absolutes — see below. + +### The defect + +`R7-observed.txt` shipped with its READING prose quoting a throughput range its OWN pinned +raw runs contradicted (prose said 369-439 M ops/s; the three pinned runs said 1.76-3.48 s, +i.e. 287-567). Operator review caught it. It was repaired — and then **R8 committed the +identical defect one commit later**: prose quoting 2417-2479 / 3959-4014 / 41.01 ms while +its own regenerated raw block held 3172-3248 / 3841-3946 / 40.36 ms. + +**Root cause is mechanical, not attentional.** The prose was hand-copied from run N while +the raw block was regenerated at run N+1. Every artifact built that way is one +regeneration away from lying about itself. "Be more careful" does not fix a method that +produces the defect by construction — twice in two consecutive commits is the proof. + +### The repair + +`valhalla-lab/reproducers/r8_report.py` GENERATES the report: it runs every arm, parses +the output it just captured, and derives every quoted range, ratio, per-pass cost and +break-even from that same output. Raw block and prose come from one subprocess result and +cannot disagree. The generated file states this at its head and instructs regeneration +rather than hand-editing. + +The README's R8 section was also rewritten to stop duplicating absolute figures at all: it +states structural results and ratios, and names the generated file as the authority. + +### Why that turned out to be the right shape, not merely a safer one + +The first regeneration produced materially different absolutes — B' moved ~25% — while +EVERY structural conclusion held identically: B ~ standalone, D > B falsified, C ~30x, +the B' collapse, the ~4.8x D'/E' recovery, the end-to-end E' win. **The stability of the +conclusions under unstable absolutes is itself the result**, and it is only visible +because the numbers are regenerable rather than pinned. A hand-pinned artifact would have +hidden it. + +### Two further corrections from the same review, both real + +1. **Toolchain was not unified.** The native kernels and standalone baseline built with + rustc 1.94.1 while the ndarray crate required 1.97.1. That left an escape hatch on the + load-bearing "one bulk FFI crossing costs nothing measurable" claim, since the control + and the arm were not the same compiler. All three artifacts now build with 1.97.1, + `-O -Ctarget-cpu=x86-64-v4 -Cdebuginfo=0`. +2. **Sweep-only comparison understated the lawful shape.** R8 first reported E' (masks) as + "2-3% slower" than D' (index lists). That compares SWEEPS, and the sweeps are tied. The + whole difference is BUILDING the population, which must be counted: + `ndarray::simd::eq_u32_strided_to_mask` (one bulk call) is an order of magnitude cheaper + than the Java scalar partition scan, so **E' wins END-TO-END on the first execution**, + moving break-even from ~120 passes to ~10 — and leaves behind a reusable mask where D' + leaves a materialized population the mask-native law forbids as internal currency. + Obeying the law is the fast path, not a tax on it. + +### The measured architecture (R6-R8), for the record + +- **R6:** the 8-byte cliff is JEP 401 BY DESIGN, not a version gap and not a flag. These + already are the JDK 27 numbers (27-jep401ea3); forcing all five flattening flags changes + nothing (`UseArrayFlattening`/`UseFieldFlattening` are `false` by DEFAULT in that build, + which is why the flags-on run had to be done rather than assumed). JEP 401 states the + cause: flattened references must be read/written atomically, capping mutable flattened + fields at 64 bits. The exemption it names is for value-class FIELDS; SoA lanes are + ARRAYS, whose elements are mutable by definition. Its speculative 128-bit note would move + the cliff to 16 B — the 12-byte register would fit, the 512-byte row would not. +- **R7:** 10^9 projections allocate 960 B TOTAL. Against R5's 65,536 ops at 800 B: + operations grew 15,000x, allocation grew 160 B — fixed scaffolding, not per-op cost. +- **R8:** five arms, checksum-identical including a standalone Rust process. Bulk FFI is + free; per-projection FFI is ~30x (the anti-JNI rule, quantified); specialization buys + NOTHING when dispatch is branch-predictable (part 1 is the control that keeps part 2 + honest); under random classids the split architecture wins ~4.8x because the selector + layer creates the information once, before the sweep. + +**Not "Java is faster than Rust"** — the winning kernels ARE Rust and so is the mask +builder. The win is specialization PLACEMENT. The durable principle: **entropy belongs +outside the hot loop**, and its scope leg — when there is no entropy, moving it buys +nothing. + ## 2026-08-25 — E-LGJ-LAYOUT-AUTHORITY-IS-TRANSFERABLE-BUT-ONLY-ABOVE-8-BYTES-1 **Status:** MEASURED (R4, R5, `valhalla-lab/reproducers/`, JDK 27 EA). From e8f0ce6ff4496c7d721273098eb2282b1f73f76e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 06:24:22 +0000 Subject: [PATCH 2/2] R8: calibrate the FFI claim, and let the generator enforce the calibration Operator review: 'bulk FFI equals standalone Rust' is broader than the comparison supports. One generated run had the in-JVM arm FASTER than the standalone process -- which cannot mean the crossing is better than free; it means process/JIT/turbo/cache context is larger than any crossing cost, so the two arms must not be called performance-identical. The banked claim is now 'one bulk Panama crossing shows no measurable penalty at this scale'. Rather than trust prose to preserve that distinction, r8_report.py DETECTS which of three cases a run lands in (in-JVM faster / slower / ranges overlap) and states it -- the same structural fix as generating the ranges themselves, applied to the interpretation. Board entry also records the shape that is the actual discovery: a mask is not metadata accompanying computation, it is partial evaluation OF it -- each mask answers a question once so the ALU never asks it again. Part 1 remains the scope leg: where there was no question, answering early buys nothing. --- valhalla-lab/reproducers/R8-observed.txt | 94 ++++++++++++------------ valhalla-lab/reproducers/README.md | 7 +- valhalla-lab/reproducers/r8_report.py | 17 ++++- 3 files changed, 70 insertions(+), 48 deletions(-) diff --git a/valhalla-lab/reproducers/R8-observed.txt b/valhalla-lab/reproducers/R8-observed.txt index 3fd9635..34823e1 100644 --- a/valhalla-lab/reproducers/R8-observed.txt +++ b/valhalla-lab/reproducers/R8-observed.txt @@ -13,42 +13,42 @@ R7, and then R8 one commit later, both shipped prose quoting a different run set their own pinned block. Regenerate instead. === rust standalone baseline (no JVM in the process) === -rust-standalone generic: 0.48 s 2086.2 M ops/s checksum 2085090877180128 -rust-standalone generic: 0.44 s 2261.8 M ops/s checksum 2085090877180128 -rust-standalone generic: 0.47 s 2132.2 M ops/s checksum 2085090877180128 +rust-standalone generic: 0.35 s 2881.4 M ops/s checksum 2085090877180128 +rust-standalone generic: 0.44 s 2271.7 M ops/s checksum 2085090877180128 +rust-standalone generic: 0.40 s 2512.8 M ops/s checksum 2085090877180128 === five arms, one JVM process, ONE native fill authority for every arm === arm ops s M ops/s checksum -A 1,000,000,000 2.19 455.7 2085090877180128 -A 1,000,000,000 3.76 266.0 2085090877180128 -A 1,000,000,000 2.21 452.7 2085090877180128 -B 1,000,000,000 0.40 2504.9 2085090877180128 -B 1,000,000,000 0.40 2488.0 2085090877180128 -B 1,000,000,000 0.40 2514.1 2085090877180128 -C 100,000,000 1.30 76.9 208554656136624 -C 100,000,000 1.19 83.7 208554656136624 -C 100,000,000 1.19 84.1 208554656136624 -D 1,000,000,000 0.43 2350.2 2085090877180128 -D 1,000,000,000 0.43 2303.4 2085090877180128 -D 1,000,000,000 0.34 2945.7 2085090877180128 +A 1,000,000,000 3.74 267.4 2085090877180128 +A 1,000,000,000 2.08 481.0 2085090877180128 +A 1,000,000,000 2.23 448.6 2085090877180128 +B 1,000,000,000 0.41 2449.8 2085090877180128 +B 1,000,000,000 0.41 2417.1 2085090877180128 +B 1,000,000,000 0.41 2437.9 2085090877180128 +C 100,000,000 1.31 76.1 208554656136624 +C 100,000,000 1.23 81.3 208554656136624 +C 100,000,000 1.21 82.4 208554656136624 +D 1,000,000,000 0.42 2377.9 2085090877180128 +D 1,000,000,000 0.42 2388.1 2085090877180128 +D 1,000,000,000 0.42 2400.5 2085090877180128 (warm-sink true) C-prefix cross-check (Java sweep @1e8): 208554656136624 -random fill: counts rails=16290 triplets=16285 quads=32961, opsPerPass=261763, passes=3820, target=999,934,660, partition scan 40.17 ms +random fill: counts rails=16290 triplets=16285 quads=32961, opsPerPass=261763, passes=3820, target=999,934,660, partition scan 39.68 ms arm ops s M ops/s checksum -A' 999,934,660 4.85 206.2 1695630190962260 -A' 999,934,660 3.23 309.7 1695630190962260 -A' 999,934,660 5.53 180.9 1695630190962260 -B' 999,934,660 1.57 638.5 1695630190962260 -B' 999,934,660 1.57 636.3 1695630190962260 -B' 999,934,660 1.57 636.2 1695630190962260 -D' 999,934,660 0.33 3059.0 1695630190962260 -D' 999,934,660 0.32 3081.9 1695630190962260 -D' 999,934,660 0.32 3120.2 1695630190962260 -mask build (ndarray::simd, one bulk FFI call): 3.092 ms; popcounts rails=16290 triplets=16285 quads=32961 (must equal the scan's counts) -E' 999,934,660 0.33 3036.5 1695630190962260 -E' 999,934,660 0.33 3021.1 1695630190962260 -E' 999,934,660 0.34 2935.6 1695630190962260 +A' 999,934,660 5.58 179.2 1695630190962260 +A' 999,934,660 3.63 275.8 1695630190962260 +A' 999,934,660 5.61 178.3 1695630190962260 +B' 999,934,660 1.56 640.2 1695630190962260 +B' 999,934,660 1.55 645.0 1695630190962260 +B' 999,934,660 1.55 643.7 1695630190962260 +D' 999,934,660 0.33 3036.5 1695630190962260 +D' 999,934,660 0.33 3041.6 1695630190962260 +D' 999,934,660 0.32 3090.3 1695630190962260 +mask build (ndarray::simd, one bulk FFI call): 2.882 ms; popcounts rails=16290 triplets=16285 quads=32961 (must equal the scan's counts) +E' 999,934,660 0.34 2942.0 1695630190962260 +E' 999,934,660 0.34 2948.7 1695630190962260 +E' 999,934,660 0.33 2997.9 1695630190962260 (warm-sink true) SYMMETRY PROOF @@ -60,24 +60,26 @@ SYMMETRY PROOF so the two population representations are provably the same SET, not merely the same size. READING -- PART 1 (period-4 classid, r & 3): THE CONTROL - B bulk FFI -> generic Rust 2488-2514 M ops/s vs standalone 2086-2262 - -> ONE BULK FFI CROSSING COSTS NOTHING MEASURABLE (same compiler, same flags). - D monomorphic kernels 2303-2946 M ops/s -- D > B FALSIFIED here. + B bulk FFI -> generic Rust 2417-2450 M ops/s vs standalone 2272-2881 + The two arms' observed ranges overlap. + -> ONE BULK PANAMA CROSSING SHOWS NO MEASURABLE PENALTY AT THIS SCALE. + (Deliberately NOT 'bulk FFI equals standalone Rust' -- see the note above.) + D monomorphic kernels 2378-2400 M ops/s -- D > B FALSIFIED here. A period-4 pattern is perfectly branch-predictable, so the generic sweep's per-row dispatch is already free; specialization cannot beat a predictor that specialized. - A Java in-process 266-456 M ops/s (~6x behind Rust, same loop) - C FFI PER PROJECTION 77-84 M ops/s = ~11.9 ns/op, ~30x slower than B. + A Java in-process 267-481 M ops/s (~5x behind Rust, same loop) + C FFI PER PROJECTION 76-82 M ops/s = ~12.3 ns/op, ~30x slower than B. The anti-JNI rule, quantified. READING -- PART 2 (random classid, SplitMix64): WHERE DISPATCH ACTUALLY COSTS - A' Java 181-310 M ops/s - B' generic Rust, per-row match 636-638 M ops/s -- a 3.9x COLLAPSE from part 1. + A' Java 178-276 M ops/s + B' generic Rust, per-row match 640-645 M ops/s -- a 3.8x COLLAPSE from part 1. The dispatch cost was always there; the branch predictor was paying it. - D' index-list partition -> mono kernels 3059-3120 M ops/s (4.84x B') - E' ndarray::simd masks -> mask sweep 2936-3036 M ops/s (4.75x B') + D' index-list partition -> mono kernels 3036-3090 M ops/s (4.73x B') + E' ndarray::simd masks -> mask sweep 2942-2998 M ops/s (4.58x B') THE FINDING - Under a distribution where dispatch actually costs, the split architecture wins ~4.7x + Under a distribution where dispatch actually costs, the split architecture wins ~4.6x -- the selector layer (classid -> ClassView -> mask) creates the information ONCE, before the sweep, where the monolithic generic loop re-derives it per row and eats the mispredict every time. @@ -85,17 +87,17 @@ THE FINDING THE COMPARISON THAT MATTERS IS END-TO-END, NOT SWEEP-ONLY (operator correction). D' and E' sweeps are effectively tied, so quoting E' as '2-3% slower' understates it. What differs is BUILDING the population, and that must be counted: - D' = build 40.17 ms (Java scalar scan -> index lists) + sweep 320.0 ms = 360.2 ms - E' = build 3.09 ms (ndarray::simd, ONE bulk call) + sweep 330.0 ms = 333.1 ms - -> the lawful mask pipeline wins END-TO-END by 8% on the FIRST execution (13.0x cheaper to build), + D' = build 39.68 ms (Java scalar scan -> index lists) + sweep 330.0 ms = 369.7 ms + E' = build 2.88 ms (ndarray::simd, ONE bulk call) + sweep 340.0 ms = 342.9 ms + -> the lawful mask pipeline wins END-TO-END by 8% on the FIRST execution (13.8x cheaper to build), and leaves behind a MASK -- reusable for and/or/andnot, authorization, traversal, attention -- where D' leaves an index list that is a materialized population the mask-native law forbids as internal currency. Amortization terms: - B' 0.4114 ms/pass; D' 0.0849; E' 0.0866 -> saving 0.3247 ms/pass vs B' - break-even for the 3.09 ms mask build : ~10 passes - break-even for the 40.17 ms Java scan : ~124 passes + B' 0.4067 ms/pass; D' 0.0861; E' 0.0888 -> saving 0.3179 ms/pass vs B' + break-even for the 2.88 ms mask build : ~9 passes + break-even for the 39.68 ms Java scan : ~125 passes WHAT THIS DOES AND DOES NOT SHOW - NOT 'Java is faster than Rust'. The winning kernels ARE Rust; the masks are built by @@ -109,4 +111,4 @@ WHAT THIS DOES AND DOES NOT SHOW STABILITY Every figure above is an observed range or a median over the raw runs printed in this - same file. Java arms spread up to 1.7x run-to-run; Rust-kernel arms a few percent. + same file. Java arms spread up to 1.8x run-to-run; Rust-kernel arms a few percent. diff --git a/valhalla-lab/reproducers/README.md b/valhalla-lab/reproducers/README.md index 990a640..a3950b3 100644 --- a/valhalla-lab/reproducers/README.md +++ b/valhalla-lab/reproducers/README.md @@ -367,7 +367,12 @@ review so "bulk FFI costs nothing" has no compiler escape hatch.) ### Part 1 — period-4 classid: the control -- **B ≈ the standalone Rust process.** One bulk FFI crossing costs nothing measurable. +- **One bulk Panama crossing shows no measurable penalty at this scale.** Stated that way + deliberately, *not* as "bulk FFI equals standalone Rust" (calibration from operator + review): one generated run had the in-JVM arm **faster** than the standalone process, + which means process/JIT/turbo/cache context is larger than any crossing cost — so the two + arms must not be called performance-identical even though no penalty is exposed. The + generator now detects which of the three cases a run lands in and says so. - **D > B is falsified.** A period-4 pattern is perfectly branch-predictable, so the generic sweep's per-row dispatch is already free — specialization cannot beat a predictor that has specialized. diff --git a/valhalla-lab/reproducers/r8_report.py b/valhalla-lab/reproducers/r8_report.py index 82a3dfb..ae465aa 100644 --- a/valhalla-lab/reproducers/r8_report.py +++ b/valhalla-lab/reproducers/r8_report.py @@ -95,7 +95,22 @@ def rng(vals, fmt="{:.0f}"): P("") P("READING -- PART 1 (period-4 classid, r & 3): THE CONTROL") P(f" B bulk FFI -> generic Rust {rng(arms['B']['m'])} M ops/s vs standalone {rng(sa)}") -P(" -> ONE BULK FFI CROSSING COSTS NOTHING MEASURABLE (same compiler, same flags).") +# Calibrated claim (operator review): assert only what the comparison supports. When the +# in-JVM arm is FASTER than the standalone process -- which has been observed -- the two +# cannot be called performance-identical; process/JIT/turbo/cache context is demonstrably +# larger than any crossing cost. What survives either way is that no penalty is exposed. +sa_med = statistics.median(sa) +if med["B"] > max(sa): + P(f" NOTE: the in-JVM arm is FASTER than the standalone process here" + f" ({med['B']/sa_med:.2f}x median). The two are therefore NOT performance-identical --") + P(" process/JIT/turbo/cache context dominates any crossing cost at this scale.") +elif med["B"] < min(sa): + P(f" NOTE: the in-JVM arm is slower here ({med['B']/sa_med:.2f}x median); check whether" + " that gap exceeds the observed run-to-run spread before attributing it to the crossing.") +else: + P(" The two arms' observed ranges overlap.") +P(" -> ONE BULK PANAMA CROSSING SHOWS NO MEASURABLE PENALTY AT THIS SCALE.") +P(" (Deliberately NOT 'bulk FFI equals standalone Rust' -- see the note above.)") P(f" D monomorphic kernels {rng(arms['D']['m'])} M ops/s" f" -- {'D > B FALSIFIED' if med['D'] <= med['B'] else 'D > B holds'} here.") P(" A period-4 pattern is perfectly branch-predictable, so the generic sweep's per-row")