finding: the mask-tail optimisation is inert on every power-of-two population, including the 4096-row tile - #316
Conversation
… population 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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DCfrD5y19cvFc4AoyydXYv
|
Warning Review limit reachedNext included review available in 15 minutes. View limit detailsLimit details: You’ve used all 2 included reviews currently available. Your 56 included PR review attempts over the past 7 days set your current allowance at 2 reviews per hour. Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Essentials Run ID: 📒 Files selected for processing (1)
Comment |
…ferred (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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DCfrD5y19cvFc4AoyydXYv
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 70ee86b3d1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ot a win 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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DCfrD5y19cvFc4AoyydXYv
Blackboard only, no code. This is the gate on the 11-function tail rewrite #315 argued for — established by arithmetic before writing it, and it changes whether that rewrite is worth doing at all.
The arithmetic
A mask over
nrows isceil(n/64)words. The facade's algebra ops walkU64x8, so a tail exists only whenwords % 8 != 0. For a power of two,words = 2^(k-6), and2^(k-6) % 8 == 0for everyk >= 9.(share = a 16 ns padded tail against a body of
groups × 0.31 ns, both from #315's own measurements)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 #315
#315 is not wrong. It is scoped narrower than it reads. Its sweep used
base = 8andbase = 64words — 512 and 4096 rows — pluskof 1..7, which is exactly the small end. Its "7 of 8 mask sizes have a tail" is a statement about arbitrary sizes, and consumer sizes are not arbitrary.So the honest reading of the merged result:
Consequence: the rewrite is not justified yet, and I had the gate wrong
I told the reviewer on #315 that the rewrite should land behind a quieter machine. That was the wrong gate. The right one is a census of real consumer population sizes — which populations actually reach
mask_andand friends, at what row counts. If they are powers of two the work is inert; if arbitrary row counts dominate it is worth ~2.6 % there and a lot at sizes like 10 000 rows.Ternlog points the other way, independently
For
mask_ternlogthe padded form is one instruction — a full-widthvpternlogq zmmover the zero-padded register. The fixed-step peel needs three logic ops at 4-lane plus more at 2-lane plus a scalar step. So for that site the padded tail may well be cheaper, which is the opposite of the direction #315 proposed, and is a second reason not to sweep all 11 sites uniformly.What still stands from #315, unchanged
The codegen witnesses are untouched:
U64x4 &really is bit-identical to a hand-written loop (the assembler merges the symbols), fixed-width peels really are packed on aarch64 as well as x86, and theavx512vlgate really is unnecessary because LLVM selects the tier itself. What is withdrawn is only the implied priority of acting on them.The class
#315's five corrections were each a label outrunning its measurement. This one is a level up: a measurement outrunning its regime. The numbers were right at the sizes probed and the conclusion was stated without them. The cheapest way to catch that is arithmetic on the size formula, not another benchmark.
Update: the census this PR called for is now run, and it CLOSES step (b)
Pushed in
483b7c1e. Read-only againstlance-graph-java'slgj-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, bimodally:
The small ones are correctness fixtures where the op is its tail and speed is irrelevant; every size large enough to have a body has zero tail. No committed test or bench in the consumer can exercise the regime the optimisation targets — which is precisely why #315's probe had to choose
base = 8and64words to see it at all.3. Where it does bite (16 ns tail, 0.31 ns/group): 85 % at 5 k rows, 35 % at 50 k, 21 % at 100 k, 5 % at ~502 k, 2.6 % at 1 M, 1 % at ~2.6 M, and 0 % on every power of two ≥ 512.
Verdict: step (b) is closed as measured-not-worth-doing, not deferred
It would touch a hot facade at 11 sites across six realizations to win something only for arbitrary populations in roughly the 5 k – 500 k row band — and it would make
mask_ternlogworse, since its padded form is one full-widthvpternlogqwhere the peel needs three ops plus more.Reopening it needs a named consumer workload whose populations sit in that band. Not another kernel benchmark.
🤖 Generated with Claude Code
https://claude.ai/code/session_01DCfrD5y19cvFc4AoyydXYv
Generated by Claude Code