Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions .claude/board/EPIPHANIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
94 changes: 48 additions & 46 deletions valhalla-lab/reproducers/R8-observed.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -60,42 +60,44 @@ 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.

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
Expand All @@ -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.
7 changes: 6 additions & 1 deletion valhalla-lab/reproducers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
17 changes: 16 additions & 1 deletion valhalla-lab/reproducers/r8_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down