Skip to content

Add I32x16::dot4_u8_i8_add_masked for exact 9-bit sign-magnitude dot products - #387

Merged
AdaWorldAPI merged 6 commits into
masterfrom
claude/u9-turbovec9-substrate
Oct 29, 2026
Merged

AdaWorldAPI merged 6 commits into
masterfrom
claude/u9-turbovec9-substrate

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

Follow-up to #386. This adds one facade word and a cost probe. The question it answers: can a native 9-bit TurboVec9 index score at close to u8 cost?

What changes

I32x16::dot4_u8_i8_add_masked(self, a: U8x64, keep: u64, b: I8x64) is dot4_u8_i8_add with the bytes of a whose bit in keep is clear replaced by 0. Two calls over the same bytes give an exact 9-bit sign-magnitude × i8 dot product. The magnitude is in a and the positive plane is in keep:

pos = pos.dot4_u8_i8_add_masked(magnitude, positive, w);
neg = neg.dot4_u8_i8_add_masked(magnitude, !positive, w);
// score lanes: pos - neg
  • Exactness: exact for every magnitude and every i8 weight, -128 included. Accumulation is in i32 and the operands are never widened.
  • No saturation: no saturating instruction is used anywhere.
  • Overflow bound: 16,448 worst-case calls fit an i32 accumulator. A test pins both sides of that bound.

How each backend realizes it:

  • AVX-512: a masked move before vpdpbusd, or the widen path when VNNI is absent.
  • AVX2: the byte mask is built in registers (variable shift, multiply, shift) and applied inside the dot loop.
  • AVX, NEON, wasm: a stored spread of the u64 mask is expanded with one AND against BIT_IN_BYTE and one byte compare per register, with no shuffle.
  • nightly: a core::simd::Mask select.
  • scalar: the reference loop.

The word is plain arithmetic, so it does not care which contract produced the bytes. DR512/Codec9 values and TurboVec9 centroid IDs are kept apart, lut9/lut_u8 are unchanged, and no codec is duplicated.

Measured (AVX-512 VNNI Xeon, examples/u9_cost_probe.rs, ranges over 3 runs)

Every exact sign-magnitude kernel must match an independent i64 reference before its time is printed. Every facade kernel folds all 16 accumulator lanes into its result.

ns / 64 lanes
u8 × i8 dot4_u8_i8_add (#386) 1.12 – 1.14
i8 × i8 (xor 0x80 bias) 1.20 – 1.25
two-limb form dot(lo) - 256·dot(sign) 1.89 – 1.92
dot(m) - 2·dot(m & neg), one accumulator 1.79 – 1.88
dot(m & pos) - dot(m & neg), two accumulators (this word) 1.28 – 1.30
512-entry lookup, 4× vpermi2b + blends 6.03 – 6.09
512-entry lookup, per-position reads 38 – 39

Precision on N(0,1), against scan cost:

layout SNR cost
i4, 16 levels (today) 20.2 dB 2.65 – 2.68 ns / 128 dims
512 Lloyd-Max IDs + in-scan lookup 49.9 dB 5.37 – 5.70 ns / 64 dims
512 Lloyd-Max levels snapped to the 9-bit grid 44.7 dB (329 distinct) 1.29 – 1.32 ns / 64 dims
uniform 9-bit grid, clip 4.21σ, stored 46.0 dB 1.29 – 1.32 ns / 64 dims
4+5 additive (C16 + R32, two i8 grids) 43.3 dB 2.61 – 2.65 ns / 64 dims

Streaming from DRAM (64 MiB of codes), the per-dimension cost is 0.035 – 0.037 ns for i4 and 0.048 – 0.052 ns for stored 9-bit.

Answer to the acceptance question: on AVX-512 VNNI, yes, with stored values. Scoring costs about the same per dimension as today's i4 permute-dot from L1, and 1.4× per dimension from DRAM, for +25.8 dB. With IDs plus an in-scan 512-entry lookup, no: the lookup costs about 5× the arithmetic. TurboVec's levels do not depend on the query, so the decode belongs at encode time.

AVX2 (no VNNI) is the weak tier. The masked pair costs 6.35 – 6.44 ns per 64 lanes, against 1.59 – 1.79 ns for one dot4. Each masked call adds about 1.4 ns of mask expansion on top of its own 16-bit widening. A fused single-widen op would avoid both the second widening and the mask; it is not built.

The Lloyd-Max codebooks in the probe are probe-local fits, not TurboVec's solver. None of these numbers are recall measurements.

Tests

  • simd::dot4_nibble_facade_parity passes 8/8 on every arm:

    • native (VNNI);
    • config-v4 (AVX-512 without VNNI);
    • config-v3 (AVX2);
    • config-avx (qemu SandyBridge);
    • config-v2 (scalar, qemu Nehalem);
    • aarch64 (qemu);
    • nightly-simd.

    The suite covers a reference corpus, per-bit gating, all 512 sign-magnitude codewords × weight edges against an i64 oracle, and the 16,448-call overflow bound.

  • The NEON and wasm parity programs pass, with new codes 0x504 and 0x505.

  • clippy -D warnings is clean on native, v4, v3 and v2.

  • Disable runs, all red as expected:

    • unmasked AVX-512 move;
    • bit-reversed BIT_IN_BYTE on AVX2;
    • mask dropped from the fused AVX2 loop;
    • wrong byte shifts for the upper AVX2 half.

Handoff

.claude/knowledge/turbovec9-u9-substrate.md carries the proposed scan layout: per 64 dims, 64 magnitude bytes plus one u64 positive plane, vector-major. It also lists the decisions that stay with the TurboVec owner: grid clip, how the encoder writes 0, and recall on real data. No TurboVec code was changed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v

dot4_u8_i8_add_masked is dot4_u8_i8_add with the bytes of `a` whose bit in
`keep` is clear replaced by 0. Two calls over the same magnitudes, one with
the positive plane and one with its complement, give an exact 9-bit
sign-magnitude dot product against i8 weights (the full i8 range, -128
included) with i32 accumulators and no widening of the operands. AVX-512
uses one masked move before vpdpbusd; AVX2, AVX, NEON and wasm expand the
mask with one AND and one byte compare per register; nightly selects
through core::simd::Mask.

examples/u9_cost_probe.rs times the candidates per 64 lanes and measures
the reconstruction error of each 9-bit layout on a unit Gaussian.

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
The AVX2 arm applies the byte mask inside the dot4 loop instead of writing a
masked copy first. The probe gains a facade section that times dot4, two
plain accumulators and the two masked accumulators on whatever tier the
build compiled.

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
@coderabbitai

coderabbitai Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ac632c3d-b5ec-42c7-8f4d-780b982af23c
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review October 29, 2026 15:51
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T15:53:21.521644Z 0c75313 Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0c7531317e

ℹ️ 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".

Comment thread examples/u9_cost_probe.rs Resolved
Comment thread examples/u9_cost_probe.rs Resolved
…ask in registers

Kernel B subtracted 256 * w for a negative zero (magnitude 0, plane bit
clear): the sign limb now skips zero magnitudes. Every exact SM9 kernel,
and the facade pair, must match an independent i64 reference before its
time is printed. The facade kernels returned lane 0 only, which let the
optimizer drop half of the AVX2 work; they now fold all 16 lanes. The AVX2
masked dot4 builds its byte mask from the u64 with a variable shift and a
multiply instead of a stored spread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
Updates the handoff tables with the checked, all-lane measurements: the
AVX2 masked pair costs 6.35-6.44 ns per 64 lanes, about 4x one dot4, not
the 4.5 ns the lane-0 timing reported. Silences dead_code on the stored
mask spread for the tiers that build the mask another way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
@AdaWorldAPI
AdaWorldAPI merged commit 0f525c2 into master Oct 29, 2026
31 checks passed
AdaWorldAPI pushed a commit that referenced this pull request Oct 30, 2026
Records D-POF-ANT-0's verdicts and the rows they move: AVX2 mask_blend
is now the loss in three probes; E2 replaces E5 as the AVX-512-without-
VNNI U9 candidate; #395's A5 oracle can serve lance-graph's X1-X9 and
#1848 B0. New dots: conjugate_mask's scalar byte expansion can reuse
mask_byte_spread/BIT_IN_BYTE from #387; the per-tier U9 kernel choice
should be one table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPh2Seh2WLccCksAdY
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants