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
47 changes: 47 additions & 0 deletions .claude/board/PR_ARC_INVENTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,53 @@
> anti-pattern the imported board rules name. Backfilled below in one
> pass rather than left stale; PR #4 onward gets its entry at merge time.

## PR #79 — `hop_cached_vs_gather`: the M1b tile pays off on hop two; the scatter walk is the access-shape question (opened 2026-09-16, merged `9cb63e9`, head `c10029b`)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mark this entry as a post-merge backfill

This entry was added in a child commit of the PR #79 merge, but it is presented as an ordinary contemporaneous entry. That makes the inventory's standing claim that PR #4 onward records its entry at merge time false and conceals the exact hygiene lapse that .claude/board/README.md:3-6,16-18 requires the durable record to expose. Future sessions may therefore treat this account as evidence recorded with the change rather than reconstructed afterward; identify it explicitly as a post-merge backfill (and preserve that correction according to the storno rule).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct — #79 was opened and merged without its entry, and presenting the reconstruction as contemporaneous would hide exactly the lapse the header promises to expose. A storno-style note now sits above the entry naming it a post-merge backfill (owned, dated, citing this review); the entry text itself is unchanged.


Generated by Claude Code


> **Post-merge backfill, owned (2026-09-16):** this entry was NOT written at
> open. #79 was opened and merged without its arc entry — the same lapse the
> header above records for PRs #1–#3 — and the entry below was reconstructed
> after the merge in PR #80 (Codex P2 on #80 named the omission). Read it as a
> reconstruction, not as a record made with the change; the numbers are the
> ones banked in the probe's module doc and `LATEST_STATE.md` at merge time.

**Added.** `native/lgj-abi/examples/hop_cached_vs_gather.rs` — `lgj_hop`'s
selection three ways at 65 536 rows × 32 facets, every arm asserted
bit-identical on every frontier before timing: `recompute` (the shipped
body: per facet two contiguous `eq_u32` passes + `ternlog<AND3>` + scatter),
`cached` (`sel_f = class_f ∧ struct_f` built once per store generation —
32 × 8 KiB = 256 KiB, the §9 M1b tile keyed `(generation, edge_classid)` —
then one `mask_and` per facet + scatter), `gather` (the scalar row walk on
AosRows, the access-shape baseline, not a candidate). `LATEST_STATE.md`
entry with the full table. No ABI symbol, no Java change.

**Measured** (Xeon 2.10 GHz, `avx512f=true`, release, median of 7, two
runs, every output `black_box`ed inside its timed closure): cache build
843–930 µs; cached **14.4–15.0 / 31.8–33.0 / 96–100 / 159–196 /
903–1 056 µs** across rand7 / rand655 / classid / hop2 / all, against
recompute 728–1 779 µs — break-even **0.9–1.3 hops** at every frontier.

**Locked.** *A store generation's predicates do not change between hops;
re-deriving them per hop is 64 contiguous 256 KiB passes for nothing.* The
scatter is `O(N/64 + selected bits)` per facet (it walks every word of
`selected` before it can know which are empty), not O(frontier) —
CodeRabbit's correction, carried into the doc and the board.

**Deferred.** Invalidation cost (a write to any classid/hi32 lane drops all
32 masks — the registry already does this wholesale for `cached_carving`);
the per-`edge_classid` multiplication of the 256 KiB; and the operator's
actual question — inside the random-access scatter walk, decide a visited
row's 32 facets with one zmm `mask_cmpeq_epi32` per 4 facets (8 loads) or
one xmm compare per facet, instead of the scalar 64-load / 64-branch loop
(`gather_xmm` / `gather_zmm_row`, named, not built).

**Review.** Codex P1 (timed closures returned `()`, outputs unread — LLVM
could drop the scatter stores): fixed, re-measured, every number held.
CodeRabbit: the O(frontier) claim, fixed; docstring-coverage warning
(40 %) on private helpers, not addressed. Bugbot: usage limit, no run.

**Confidence.** HIGH on the numbers (two runs, ranges banked). The
access-granularity arms are unmeasured; the door is named, not opened.

## PR #77 — first CI lint gate: fmt + clippy + rust-test, Rust pinned to 1.98.1 (opened 2026-09-05, head `6d4b1a2`)

**Added.** `.github/workflows/lint.yml` (three jobs: `format`, `clippy`,
Expand Down
2 changes: 1 addition & 1 deletion native/lgj-abi/examples/hop_cached_vs_gather.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
//! | hop2 | 6 943 | 12 125 | 1 019–1 102 | **159–196** | 705–758 | 0.9–1.1 hops |
//! | all | 65 536 | 56 841 | 1 630–1 779 | **903–1 056** | 3 583–3 863 | 1.2–1.3 hops |
//!
//! **The cached tile pays for itself on the second hop** (break-even 1.0–1.4
//! **The cached tile pays for itself on the second hop** (break-even 0.9–1.3
//! hops at every frontier): a store generation's predicates do not change
//! between hops, and the shipped body re-derives them with 64 contiguous
//! 256 KiB passes per hop. What remains per hop is one 8 KiB `mask_and` per
Expand Down
Loading