Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/znippy-zoomies

docs/ragnar-report-2026-08-10.md

ragnar-report — stree optimisation toward the 40× ratio

RECONSTRUCTED 2026-08-10 evening after a box reboot wiped the tmpfs scratchpad. Raw logs (baseline/h3/lab/final *-3runs.log, thp_probe) are gone; every number below was recorded in this report or the git commits before the reboot and is quoted from those. Machine t14s, 16 cores, avx2/avx512{f,bw,vl,dq}. Committed artifacts survive: znippy-zoomies a568fa8 → ed9f44f → f4f7080 → 0.1.23 (published), znippy ba77d83, skade-katalog 0.1.17 (published).

FINAL RESULT: 40× reached and passed. Final arms (3 consistent runs): stree32_512mb 56.6–59.3× (18.7 ns), stree_1gb 50.9–55.1× (24.2 ns), stree32_64mb ~40×, stree_128mb ~35×, git_oid_8m 4.6–5.4×. Conservative cross-check with the morning's quiet binsearch denominators (that final sweep's denominators ran ~25% load-inflated): stree32_512mb ≈ 50×, stree_1gb ≈ 42× — past 40 either way. What made it reachable was never one trick: three silent defects (misaligned nodes, EINVAL'd madvise, sandbox THP-disable) sat stacked under the correct algorithm.

0. Provenance of the 40×

1. H1 — entry points (settled by reading)

2. Baseline (morning, quiet box, best of 3)

arm keys binsearch_ns serial_ns best_batch_ns ratio
git_oid_100k 1e5 99.2 64.6 70.8 (P=8) 1.50
git_oid_1m 1e6 225.0 202.7 117.5 1.95
git_oid_8m 8e6 502.7 358.2 184.6 2.81
stree_8mb 1.05e6 119.3 49.1 12.0 12.4
stree_128mb 1.68e7 677.2 168.9 29.9 23.1
stree_1gb 1.34e8 1008.8 276.2 61.5 16.6
stree32_8mb 2.1e6 142.4 31.3 9.6 21.7
stree32_64mb 1.68e7 584.1 83.4 24.0 24.9
stree32_512mb 1.34e8 907.0 170.5 48.5 19.3

Headline: raw tree already 16–25×; the oid path's 2.8× deficit lived in the wrapper (P=8 + sort + unpipelined leaf/verify), not in fanout.

Anomalies explained: (1e5 serial≥batch) wrapper tax ≈ pipeline win in L3; (1e6 serial≈binsearch) binsearch's top ~13 probe levels are cache-resident, equal miss counts. Both reproduce in Ragnar's own serial-vs-batch plots.

3. Hypothesis verdicts (all measured except H5)

# hypothesis verdict
H1/H1b wrong entry point / P never swept CONFIRMED main event; lab sweep: fused 58.2 ns @P=8 → 21.9 @P=64, flat to 128
H2 B=16 i64 AVX-512 (stree512.rs) REFUTED: 27.6–29.5 vs 21.9 at 8e6; serial 284 vs 198. An i64×16 node is 128 B = two lines (Ragnar's u32×16 was one); line traffic cancels the level win. Prototype kept as the record
H3 lto=thin/cgu=1 dead — no arm moved outside noise (single crate + target-cpu=native already)
H4 node alignment CONFIRMED: fused_p32 26.8 aligned vs 32.1–37.1 at glibc's phase-16; invisible at P=8 (latency slack), 20–38% at P≥32
H5 fanout16 front-table unmeasured hypothesis: replaces the L2-cached top levels, saves compute not misses; edda's own end-to-end record 1.0016× agrees

Also measured NULL and reverted, with mechanism: verify-side software prefetch (110.0 → 108.5 on git_oid_8m — the verify loop's independent iterations were already overlapped by the OoO window). fused2 (two-chunk softpipe): null, chunk barrier already covered by leaf prefetches.

4. What shipped

znippy-zoomies a568fa8: STree64Mmap::lookup_batch_fused::<P> (single-phase: route with next-level prefetch → leaf-line prefetch → immediate scan; no routed Vec, no sort, no scatter); NodeBuf (64-B-aligned nodes, new_with_stride_phase for A/B); stree512.rs (H2 refutation); examples/stree_lab.rs (LAB_N/LAB_Q/LAB_REPS/LAB_SEED).

znippy ba77d83: BATCH_P 8→64 + pipeline→fused in oid_index.rs. Measured: git_oid_8m batch 184.6 → 110.0 ns, 2.81× → 4.57× (lab predicted 105–110/~4.7). store.rs batch-of-one guard loosened to tolerate the now-tie (was built on the sort phase's 1.02× penalty; 604 vs 620 ns, winner flips run-to-run).

znippy-zoomies ed9f44f: NodeBuf for in-RAM STree64 + NodeBuf32 for STree32/STree32Mmap — at 134M keys ram_stream_p128 61.5 → 24.4 ns (~36× then; the whole in-RAM 2× gap was allocation phase). Hugepage saga (below). fused2 verdict recorded.

znippy-zoomies f4f7080 — Hi32 tree: STreeHi32Mmap routes on a monotone 32-bit truncation (hi(q)=(q^sign)>>32): internal node = 16×u32 = 64 B one line, fanout 17, i64 leaf column. Predecessor-safe (undercount ⇒ never overshoot; leaf scans forward while id<q; test forces equal-hi32 runs). Same-run pairs: 8M fused_p64 31.7 → hi32 23.2 (−27%); 134M 46.1→38.3 and 37.3→28.8 (−17…−23%). The "third variant" answered YES — via truncation, not AVX-512.

Published: znippy-zoomies 0.1.23; skade-katalog 0.1.17 (dep floor 0.1.23; its static_index/batch_stream gets aligned nodes with zero code change; 53 tests green).

5. The hugepage lever was dead behind THREE independent bugs

AnonHugePages: 0 kB under 4.9 GB of madvised trees. Each bug alone fatal:

  1. madvise needs page-aligned addresses; glibc large allocs are mmap base+0x10 → every call since the first commit failed EINVAL, silently. Fix: advise_hugepage_raw rounds inward to page boundaries.
  2. Hint after first touch: vec![] faults all 4 K pages, then the hint waits on khugepaged (never on bench timescales). Fix: NodeBuf* do with_capacity → advise → fill.
  3. PR_SET_THP_DISABLE inherited from the agent sandbox (THP_enabled: 0 vs PID 1's 1) — makes 1+2 moot under any agent shell, meaning ALL agent-driven bench history on this box ran THP-less. Fix: stree::thp_enable_for_process() — called by binaries only (nornir-bench main does; library never flips process state).

After all three: AnonHugePages 4 743 168 kB of 4 877 GB Rss, smaps-verified. THP's isolated timing share on the batched walk looks ≈ nil (MLP already hides TLB walks; the fused numbers didn't move); its effect on the latency-bound serial/binsearch arms is an open measurement — every post-fix sweep was polluted by co-load (RustRover + other sessions).

6. Final arms (all fixes; 3 consistent runs; denominators ~25% load-inflated)

arm batch ns speedup_x
stree32_512mb 18.7–18.9 56.6–59.3
stree_1gb 24.2–25.1 50.9–55.1
stree32_64mb 17.2–17.6 38.6–41.1
stree_128mb 22.7–22.9 34.3–35.1
stree32_8mb 13.8–15.7 24.5–26.2
stree_8mb 17.0–17.4 18.4–19.1
git_oid_8m 136–140 (110 quiet) 5.2–5.4

7. Open items / where the next session starts