Repository navigation
U9 × U9 head-to-head: U8×U8×sign×mul_add against U8×I8 byte-dot (probe + TurboVec-U9 handoff) - #388
Conversation
…U8 x I8 byte-dot Both candidates read the same block per operand (64 u8 magnitudes, one u64 positive plane) and are written only with ndarray::simd facade words. A keeps the magnitudes unsigned, routes each product by plane_a ^ plane_b and sums in f32 (mul_add, or U16x32::mullo then add), exact below 2^24 and flushed every 256 blocks. B splits |b| into its low seven bits and its top bit, signs both and runs two dot4_u8_i8_add, flushed every 1024 blocks. Scan forms prepare a query block once and stream index blocks; B's scan form routes the index sign with dot4_u8_i8_add_masked. tests/u9_head_to_head.rs checks every candidate against one i64 oracle on all 511 x 511 numeric pairs, full mixed blocks, worst-case runs past both flush intervals, and refuses the never-touched codeword in either operand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
B no longer negates bytes or narrows through packs. |b| is split with AND and XOR (low7 = |b| & 0x7F, top = |b| ^ low7, read as signed bytes top is -128), the sign stays in the presence plane and is flipped with XOR (plane_a ^ plane_b), and dot4_u8_i8_add_masked applies it as the byte mask: four masked byte-dots per block, (pos_low7 - pos_top) - (neg_low7 - neg_top). The symmetric and scan forms now share one accumulator. The facade has no U8x64 -> I8x64 view, so bits_as_i8 stands in with a store and a load. Lane totals leave through a stored array read lane by lane, held apart with black_box so the compiler does not rebuild a permute reduction. Disassembly of the timed closures: no shuffle, permute, pack, extract or insert in any B, baseline or flush path; A keeps the widening moves its u8 -> u16 -> f32 path needs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
B now wins every row of the head-to-head, symmetric included. The report and the blackboard describe the AND/XOR split, the masked byte-dots, the shuffle-free flush, the new tables, and the one remaining facade gap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
Both candidates implement one contract: unsigned magnitude product, polarity XOR as a routing decision, exact bounded fold. B is that product decomposed like long multiplication. The report now gives the exact domain and stage bounds, says what is not proven, and drops the claim that A cannot be written without the widening instructions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
On an AVX2 build LLVM emits four vpshufb inside the facade's masked byte-dot to spread the keep mask; the earlier statement was measured on the AVX-512 build only and read as if it held everywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8f66df9d6e
ℹ️ 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".
The timed loops streamed Vec<Block>, which pads each 72-byte block to 128 bytes because U8x64 is 64-byte aligned. Operands are now two compact arrays (magnitudes, planes) and each Block is built in registers. The preparation row passes the whole prepared tuple through black_box, so no lane of low7 or top can be dropped as unused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
The streamed rows were inflated by the padded Vec<Block> layout; on compact 72-byte blocks the VNNI B end-to-end row is 6.0-6.5 ns (was 12.9-16.3) and the B scan 2.75-2.80 (was 5.9-6.9). The preparation row now includes storing the whole prepared tuple. Verdict unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
The abacus probe streams the same operands and needs the same layout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
Summary
This probe compares two ways of computing an exact signed 9-bit (-255..=255) dot product using only the existing
ndarray::simdfacade. Both read the same input per operand: 64u8magnitudes and oneu64polarity plane.The arithmetic contract
For each lane pair:
Magnitudes are never negated. The polarity XOR only chooses which side of the fold each product joins. The two candidates are different facade compositions of this same contract: neither is more exact than the other, and they produce the same results.
mul_add. The whole productm_a * m_bis formed inf32after widening (A' forms it inu16withmullo), then added to the positive or the negative sum according to the XOR plane.low7 = m_b & 0x7F;top = m_b ^ low7, read as a signed byte, so it is 0 or -128;m_b = low7 - topandm_a * m_b = m_a * low7 - m_a * top;255 × 255 = 32,385 + 32,640 = 65,025;dot4_u8_i8_add_maskedcalls run per block, with the XOR plane as the mask.No production code changed. The work is
examples/u9_head_to_head/{kernels,main}.rs,tests/u9_head_to_head.rs, and the report.claude/knowledge/turbovec9-u9-head-to-head.md.Result
B is faster in every row, including the symmetric per-block row. Figures are ns per 64 values, data in L1, ranges over 3 runs.
The streamed and arithmetic-only rows are in the report.
A's cost comes from how this composition lowers through the current facade: widening u8 → u16 → f32, and on AVX2,
U8x64::mask_blend. It is not a property of U8 × U8 multiplication. Other lowerings of A were not explored. On the AVX-512 builds, the disassembly shows no shuffle, permute, pack, extract or insert in B, in the baselines, or in the flushes. On AVX2 that is not true: LLVM emits 4vpshufbinside the facade's masked byte-dot (dot4_u8_i8_add_masked), which it uses to spread the bytes of the keep mask. That instruction comes from the facade's own AVX2 code, not from this probe, and was not changed here.Exactness
Both candidates are bit-exact (no rounding, saturation or wrap) under two conditions:
check, which callers run before the kernel);i64, which holds up to about 2.2 × 10¹² worst-case blocks.The bounds at each stage:
i32::MAXTests, all checked against one independent
i64oracle:top = -128and the byte-dot would saturate if it could;prepare_b.low7 XOR topgives back the original byte,low7 - topgives back its value, and changing a polarity bit changes only the routing plane, never the bytes.The tests pass on native, v4, v3, AVX (qemu), v2 (qemu), aarch64 (qemu) and nightly-simd. I broke each of twelve checks on purpose, and every time the tests failed. CI runs this test file only in its x86 test jobs. The other tiers were run locally.
Not proven here:
i64total;Facade gap (recorded, not filled)
The facade has no
U8x64 → I8x64bit view. As a stand-in,bits_as_i8stores the 64 bytes and loads them back as signed; its cost is inside B's operand preparation. It is not added to the facade because it crosses between u8 and i8.TurboVec-U9 handoff
The report keeps Arithmetic511 (DR512: 511 values plus the never-touched sentinel) separate from Codebook512 (512 distinct IDs). An exact arithmetic kernel does not make Lloyd-Max512 decoding lossless. #387 measured 329 distinct values after snapping, and the lookup cost is reported.
https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
Generated by Claude Code