From 70ee86b3d11a7950c510634e09506976845e1c0d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 16 Sep 2026 23:46:08 +0000 Subject: [PATCH 1/3] blackboard (18): the tail optimisation is inert on every power-of-two population MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Established before writing the rewrite #315 argued for, because it changes whether that rewrite is worth doing. No code — this entry is the gate. A mask over n rows is ceil(n/64) words, and the facade's algebra ops walk U64x8, so a tail exists only when words % 8 != 0. For a power of two, words = 2^(k-6), which is divisible by 8 for every k >= 9. Measured against a 16 ns padded tail and a 0.31 ns/group body: 512 rows 8 words no tail 4096 rows 64 words no tail <- the canonical tile 10 000 rows 157 words 5-word tail, 73% of the op 65 536 rows 1024 words no tail 1 000 000 15625 w 1-word tail, 2.6% of the op 16 777 216 262144 w no tail So every power-of-two population at or above 512 rows has NO TAIL AT ALL, and this workspace's shapes are overwhelmingly powers of two: the 4096-row tile, the 64-word mask, the 512-byte node, the 4096-centroid codebook. #315's measurement is not wrong, it is scoped narrower than it reads. The sweep used base = 8 and 64 WORDS — 512 and 4096 rows — so "the padded tail is routinely larger than the work it trails" holds at 8-71 words and becomes 2.6% on a million-row table and 0% on the canonical tile. Its "7 of 8 mask sizes have a tail" is a statement about arbitrary sizes; consumer sizes are not arbitrary. Consequence: the 11-function rewrite is not justified yet, and the gate is NOT "a quieter machine" as I told the reviewer on #315 — it is a census of real consumer population sizes. Ternlog points the other way independently: its padded form is ONE full-width vpternlogq where the fixed-step peel needs three logic ops plus more, so a uniform sweep of all 11 sites would make that site worse. The class: #315's corrections were each a label outrunning its measurement. This is one level up — a measurement outrunning its REGIME. The numbers were right at the sizes probed and the conclusion was stated without them. Arithmetic on the size formula answers it faster than another benchmark. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01DCfrD5y19cvFc4AoyydXYv --- .claude/blackboard.md | 78 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/.claude/blackboard.md b/.claude/blackboard.md index 6c539dce..5138510c 100644 --- a/.claude/blackboard.md +++ b/.claude/blackboard.md @@ -1,3 +1,81 @@ +## 2026-09-16 (18) — the tail OPTIMISATION IS INERT ON EVERY POWER-OF-TWO POPULATION, including the 4096-row tile + +Established before writing the rewrite (15)-(17) argued for, and it changes +whether that rewrite is worth doing at all. No code; this entry is the gate. + +### The arithmetic + +A mask over `n` rows is `ceil(n/64)` words (`lgj-abi`'s `mask_words_for`, and +this file's own `mask_words_for`). The facade's algebra ops walk `U64x8`, so a +TAIL exists only when `words % 8 != 0`. For a power of two, `words = 2^(k-6)`, +and `2^(k-6) % 8 == 0` for every `k >= 9`. So: + +| rows | words | tail | tail share of the whole op | +|---:|---:|---:|---:| +| 512 | 8 | 0 | **none** | +| **4096** | **64** | **0** | **none** | +| 10 000 | 157 | 5 | 73 % | +| 65 536 | 1024 | 0 | **none** | +| 1 000 000 | 15 625 | 1 | 2.6 % | +| 16 777 216 | 262 144 | 0 | **none** | + +(share computed as 16 ns padded tail against a body of `groups × 0.31 ns`.) + +**Every power-of-two population at or above 512 rows has NO TAIL AT ALL**, and +this workspace's shapes are overwhelmingly powers of two — the 4096-row tile, +the 64-word mask, the 512-byte node, the 4096-centroid codebook. + +### What this does to (15)-(17) + +Those entries are not wrong; they are **scoped narrower than they read.** The +probe swept `base = 8` and `base = 64` WORDS — 512 and 4096 rows — plus `k` of +1..7, i.e. exactly the small end, and its "7 of 8 mask sizes have a tail" is a +statement about ARBITRARY sizes. The consumers' sizes are not arbitrary. + +So the honest reading of the merged measurement: + +- "the padded tail is routinely larger than the work it trails" — true **at + 8-71 words**, which is 512-4544 rows. +- On a million-row table the same tail is **2.6 %**. +- On the canonical 4096-row tile it is **0 %** — the op has no tail to pay for. + +### Consequence: the 11-function rewrite is NOT justified yet + +It would touch a hot facade across six realizations to win 0 % on the canonical +shape, ~2.6 % on a large arbitrary one, and a lot on sizes like 10 000 rows. +Whether that last case is real is a CONSUMER question nobody has asked yet: +which populations actually reach `mask_and` and friends, at what row counts. + +**The gate for step (b) is therefore not "a quieter machine" (what I told the +reviewer on #315) — it is a census of real consumer population sizes.** If they +are powers of two, the work is inert and should not be done; if arbitrary row +counts dominate, it is worth ~2.6 % there and much more on small masks. + +### And ternlog specifically points the other way + +For `mask_ternlog` the padded form is **ONE** instruction — a full-width +`vpternlogq zmm` over the zero-padded register. The fixed-step peel measured in +(16) needs **three** logic ops at 4-lane plus more at 2-lane plus a scalar +step. So for ternlog the padded tail may well be the CHEAPER shape, which is +the opposite of the direction (15) proposed for it, and is an independent +reason not to sweep all 11 sites uniformly. + +### The rule this is an instance of + +(15)-(17) were each about a label outrunning its measurement. This one is one +level up: **a measurement outrunning its REGIME.** The numbers were correct at +the sizes probed and the conclusion was stated without them. Before a kernel +optimisation lands, ask what the consumer's actual shapes are — a 3x win on a +size nobody runs is worth nothing, and the cheapest way to find that out is +arithmetic on the size formula, not another benchmark. + +Cross-ref: (15) the trip-count finding and the codegen witnesses stand +unchanged — `U64x4 &` really is bit-identical to a hand-written loop, fixed +peels really are packed on aarch64, the `avx512vl` gate really is unnecessary. +What is withdrawn is only the implied priority of acting on them. + +--- + ## 2026-09-16 (17) — ⊘ CORRECTS (16) twice: the unit was OBSERVATIONS not widths, and "lower bound" was one claim too many Both caught by coderabbit on the same PR (#315), both are errors (16) introduced From 483b7c1e3bb4471ba6a9cb14cf6b428d476a38da Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 16 Sep 2026 23:49:37 +0000 Subject: [PATCH 2/3] =?UTF-8?q?blackboard=20(18):=20run=20the=20consumer?= =?UTF-8?q?=20census=20=E2=80=94=20step=20(b)=20is=20closed,=20not=20defer?= =?UTF-8?q?red?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (18) named a census of real consumer population sizes as the gate. Run it, read-only, against lgj-abi — the binding consumer of the facade's mask algebra. 1. There is NO tiling of the population. `lgj_pattern_open(n_rows, ...)` takes the row count directly and a mask spans the whole population; the only "tile" in that crate is the 12-byte facet register, not the rows. So the padded tail is paid once per op and amortizes over the entire body — the favourable direction for the status quo. 2. Every committed population size is blind to the tail, bimodally: 4..200 rows are 1-4 words, i.e. ALL TAIL with not one full group (correctness fixtures where speed is irrelevant), while 500 / 1000 / 1024 / 4096 / 64000 rows are 8 / 16 / 16 / 64 / 1000 words — every one divisible by 8, so ZERO tail. No committed test or bench can exercise the regime the optimisation targets, which is why #315's probe had to pick base = 8 and 64 words to see it. 3. Where it bites, as a share of the op: 85% at 5k rows, 35% at 50k, 21% at 100k, 5% at ~502k, 2.6% at 1M, 1% at ~2.6M, and exactly 0% on every power of two at or above 512. Verdict: step (b) is closed as measured-not-worth-doing rather than deferred. It would touch a hot facade at 11 sites across six realizations to win something only for arbitrary populations in the ~5k-500k band, and would make `mask_ternlog` worse — its padded form is one full-width vpternlogq where the peel needs three ops plus more. Reopening it needs a named consumer workload whose populations sit in that band, not another kernel benchmark. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01DCfrD5y19cvFc4AoyydXYv --- .claude/blackboard.md | 56 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/.claude/blackboard.md b/.claude/blackboard.md index 5138510c..8390161d 100644 --- a/.claude/blackboard.md +++ b/.claude/blackboard.md @@ -69,6 +69,62 @@ optimisation lands, ask what the consumer's actual shapes are — a 3x win on a size nobody runs is worth nothing, and the cheapest way to find that out is arithmetic on the size formula, not another benchmark. +### The consumer census (18) called for — run, and it closes the question + +Done read-only against `lance-graph-java`'s `lgj-abi`, the binding consumer of +the facade's mask algebra. + +**1. There is NO tiling of the population.** `lgj_pattern_open(n_rows, …)` / +`lgj_rowstore_open(n_rows, …)` take the row count directly and a mask spans the +WHOLE population (`abi.rs::mask_words_for` = `n_rows.div_ceil(64)`). The only +"tile" in that crate is the 12-byte facet register, not the rows. So the padded +tail is paid ONCE per op and amortizes over the entire body — the favourable +direction for the status quo. + +**2. Every committed population size is blind to the tail**, and in a +bimodal way that is worth seeing laid out: + +| rows | words | can it see a tail? | +|---:|---:|---| +| 4, 8, 10, 16, 32, 64 | 1 | **all tail** — not one full group | +| 70, 100, 128 | 2 | **all tail** | +| 200 | 4 | **all tail** | +| 500 | 8 | none | +| 1000, 1024 | 16 | none | +| 4096 | 64 | none | +| 64000 | 1000 | none | + +The small sizes are correctness fixtures where the op IS its tail and speed is +irrelevant; every size large enough to have a body has **zero tail**. So no +committed test or bench in the consumer can ever exercise the regime the +optimisation targets — which is exactly why #315's probe had to choose +`base = 8` and `64` words to see it at all. + +**3. Where it does bite, as a share of the op** (16 ns tail, 0.31 ns/group): + +| population | tail share | +|---:|---:| +| 5 000 rows | 85 % | +| 50 000 | 35 % | +| 100 000 | 21 % | +| **~502 000** | **5 %** | +| 1 000 000 | 2.6 % | +| ~2 616 000 | 1 % | +| any power of two ≥ 512 | **0 %** | + +### Verdict on step (b): DO NOT BUILD IT + +The optimisation is worth something only for arbitrary populations in roughly +the **5 k – 500 k row** band. Outside it: under 512 rows the op is all tail and +untimed, above ~2.6 M rows it is under 1 %, and on every power of two it is +exactly zero. Against that it would touch a hot facade at 11 sites across six +realizations, and make `mask_ternlog` WORSE (its padded form is one full-width +`vpternlogq`; the peel is three ops plus more). + +**So step (b) is closed as measured-not-worth-doing, not deferred.** Reopening +it needs a named consumer workload whose populations sit in that band — not +another kernel benchmark. + Cross-ref: (15) the trip-count finding and the codegen witnesses stand unchanged — `U64x4 &` really is bit-identical to a hand-written loop, fixed peels really are packed on aarch64, the `avx512vl` gate really is unnecessary. From 9b3dd5b3266a8149f6ec2d2c577de5de85cd3fde Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 16 Sep 2026 23:52:15 +0000 Subject: [PATCH 3/3] blackboard (18): the headline dropped its condition, and a share is not a win MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two codex findings on #316, both correct, both against this entry. (a) The headline read "inert on every power-of-two population". False below 512 rows: 64/128/256 rows are 1/2/4 words, so words % 8 != 0 and the tail is the WHOLE op. The derivation immediately above the table says k >= 9 and the prose below said "at or above 512 rows" — the TITLE is what lost it. That is the fourth level of this session's one defect class: after a label outrunning its accumulator, a claim outrunning its evidence, and a measurement outrunning its regime, a headline outrunning the derivation directly beneath it. (b) A SHARE is not a WIN. The 16 ns is the padded tail's share of the current op; the rewrite does not remove it, it replaces it with #315's measured fixed-step tail of 5.33 ns. Every share figure therefore overstated the win by exactly 16 / 10.67 = 1.5x. At a million rows: current 1953 x 0.31 + 16 = 621 ns, win 10.67 ns = 1.7%, not 2.6%. Both tables now carry share AND win columns, and the crossovers move in: the win falls under 5% above ~325k rows and under 1% above ~1.74M, where the share put those at ~502k and ~2.6M. The verdict is unchanged and strengthened — the band where the work pays is narrower than the entry first claimed. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01DCfrD5y19cvFc4AoyydXYv --- .claude/blackboard.md | 72 +++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 23 deletions(-) diff --git a/.claude/blackboard.md b/.claude/blackboard.md index 8390161d..0b82496c 100644 --- a/.claude/blackboard.md +++ b/.claude/blackboard.md @@ -1,4 +1,4 @@ -## 2026-09-16 (18) — the tail OPTIMISATION IS INERT ON EVERY POWER-OF-TWO POPULATION, including the 4096-row tile +## 2026-09-16 (18) — the tail OPTIMISATION IS INERT ON EVERY POWER-OF-TWO POPULATION >= 512 ROWS, including the 4096-row tile Established before writing the rewrite (15)-(17) argued for, and it changes whether that rewrite is worth doing at all. No code; this entry is the gate. @@ -10,16 +10,35 @@ this file's own `mask_words_for`). The facade's algebra ops walk `U64x8`, so a TAIL exists only when `words % 8 != 0`. For a power of two, `words = 2^(k-6)`, and `2^(k-6) % 8 == 0` for every `k >= 9`. So: -| rows | words | tail | tail share of the whole op | -|---:|---:|---:|---:| -| 512 | 8 | 0 | **none** | -| **4096** | **64** | **0** | **none** | -| 10 000 | 157 | 5 | 73 % | -| 65 536 | 1024 | 0 | **none** | -| 1 000 000 | 15 625 | 1 | 2.6 % | -| 16 777 216 | 262 144 | 0 | **none** | - -(share computed as 16 ns padded tail against a body of `groups × 0.31 ns`.) +| rows | words | tail | tail SHARE of the op | what the REWRITE would WIN | +|---:|---:|---:|---:|---:| +| 64 | 1 | 1 | 100 % | 67 % | +| 128 | 2 | 2 | 100 % | 67 % | +| 256 | 4 | 4 | 100 % | 67 % | +| **512** | **8** | **0** | **none** | **none** | +| **4096** | **64** | **0** | **none** | **none** | +| 10 000 | 157 | 5 | 73 % | 49 % | +| 65 536 | 1024 | 0 | **none** | **none** | +| 1 000 000 | 15 625 | 1 | 2.6 % | **1.7 %** | +| 16 777 216 | 262 144 | 0 | **none** | **none** | + +⊘ **Two corrections, both codex on #316, both to this table and its headline.** + +**(a) The headline dropped its own condition.** It read "inert on every +power-of-two population". False below 512 rows: 64/128/256 rows are 1/2/4 +words, so `words % 8 != 0` and the tail is the WHOLE op. The derivation right +above the table says `k >= 9` and the prose below said "at or above 512 rows" — +the TITLE is what lost it, which is the fourth level of this session's one +defect class: after a label outrunning its accumulator, a claim outrunning its +evidence and a measurement outrunning its regime, **a headline outrunning the +derivation directly beneath it.** + +**(b) A SHARE is not a WIN, and this entry conflated them.** The 16 ns is the +padded tail's share of the current op; the rewrite does not remove it, it +replaces it with #315's measured fixed-step tail of **5.33 ns**. So the win is +`(16 − 5.33) / current`, and every share figure overstated it by exactly +`16 / 10.67 = 1.5×`. At a million rows: current `1953 × 0.31 + 16 ≈ 621 ns`, +win `10.67 ns ≈ 1.7 %` — not 2.6 %. **Every power-of-two population at or above 512 rows has NO TAIL AT ALL**, and this workspace's shapes are overwhelmingly powers of two — the 4096-row tile, @@ -100,22 +119,29 @@ committed test or bench in the consumer can ever exercise the regime the optimisation targets — which is exactly why #315's probe had to choose `base = 8` and `64` words to see it at all. -**3. Where it does bite, as a share of the op** (16 ns tail, 0.31 ns/group): - -| population | tail share | -|---:|---:| -| 5 000 rows | 85 % | -| 50 000 | 35 % | -| 100 000 | 21 % | -| **~502 000** | **5 %** | -| 1 000 000 | 2.6 % | -| ~2 616 000 | 1 % | -| any power of two ≥ 512 | **0 %** | +**3. Where it does bite** — reported as the WIN the rewrite would deliver, not +the tail's share, per the correction above (padded 16 ns replaced by #315's +measured fixed-step 5.33 ns, against a body of `groups × 0.31 ns`): + +| population | tail share | REWRITE WIN | +|---:|---:|---:| +| 5 000 rows | 85 % | 57 % | +| 50 000 | 35 % | 23 % | +| 100 000 | 21 % | 14 % | +| ~325 000 | 7.5 % | **5 %** | +| ~502 000 | 5 % | 3.4 % | +| 1 000 000 | 2.6 % | **1.7 %** | +| ~1 735 000 | 1.5 % | **1 %** | +| any power of two ≥ 512 | 0 % | **0 %** | + +So the crossovers move in with the correction: the win falls under **5 %** above +~325 k rows and under **1 %** above ~1.74 M, where the share figures put those +thresholds at ~502 k and ~2.6 M. ### Verdict on step (b): DO NOT BUILD IT The optimisation is worth something only for arbitrary populations in roughly -the **5 k – 500 k row** band. Outside it: under 512 rows the op is all tail and +the **5 k – 325 k row** band (where the WIN, not the share, clears 5 %). Outside it: under 512 rows the op is all tail and untimed, above ~2.6 M rows it is under 1 %, and on every power of two it is exactly zero. Against that it would touch a hot facade at 11 sites across six realizations, and make `mask_ternlog` WORSE (its padded form is one full-width