Skip to content

Complete the U16x32 facade surface on every backend - #385

Merged
AdaWorldAPI merged 2 commits into
masterfrom
claude/u16x32-facade-gaps
Oct 29, 2026
Merged

AdaWorldAPI merged 2 commits into
masterfrom
claude/u16x32-facade-gaps

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

ndarray::simd::U16x32 had a different method set on each backend, and there was no way to get from U16x32 to F32x16. Code written against the facade couldn't use zero(), min/max, saturating arithmetic, the compare masks or the u16→f32 widening portably. This gives every backend the same surface and fixes two AVX-512 bugs the new parity test found.

Consumer: turbovec's AVX2 FastScan kernel, rewritten against ndarray::simd at 64-byte width (U8x64 loads → U16x32 accumulators → F32x16 flush). Its flush needs to_f32x16_lo/to_f32x16_hi.

Added (AVX-512, AVX2, AVX-without-AVX2, scalar, nightly):

  • zero, simd_min/simd_max, saturating_add/saturating_sub
  • cmpeq_mask/cmpgt_mask returning a u32 bitmask (lane i in bit i)
  • reduce_min/reduce_max
  • to_f32x16_lo/to_f32x16_hi (every u16 is exact in f32)
  • AVX-512 only: BitAnd/BitOr/BitXor (+ *Assign), Not, SubAssign, Default, which the other backends already had
  • nightly only: from_u8x64_lo/from_u8x64_hi, shl/shr, mullo, pack_saturate_u8, which the other backends already had

How each backend realizes them:

  • AVX2: two 256-bit halves, gated off the AVX-without-AVX2 arm. The unsigned > is max(a, b) == a && a != b, so no signed compare is involved. Reductions fold to 128 bits and finish with minpos_epu16.
  • AVX-without-AVX2: simd_avx.rs supplies the same methods from four 128-bit SSE quarters.
  • Scalar: per-lane loops, which the facade-stride rule allows only in this backend.

Changed:

  • U16x32::reduce_sum returns u16 (wrapping) on AVX-512 as well. It returned u32 there and u16 on every other backend. No caller in this repo.

Fixed (both pre-existing on AVX-512):

  • shl/shr returned 0 for every count other than 1, 2, 4 and 8, including shl(0). They now use the variable-count sll_epi16/srl_epi16, which also gives 0 for counts ≥ 16, matching the other backends.
  • pack_saturate_u8 used packus_epi16, which saturates as signed (a lane ≥ 0x8000 became 0) and interleaves its two inputs per 128-bit lane. It now uses cvtusepi16_epi8 per input, so bytes 0..32 come from self and 32..64 from other, as on the other backends. No caller in this repo.

Parity test: simd::u16x32_facade_parity covers every method against a plain-array reference. The corpus mixes a fixed-seed generator with the edge values 0, 1, 255, 256, 0x7FFF, 0x8000 and 0xFFFF, and checks more than 5000 pairs for the element-wise ops.

arm how result
AVX-512 default build (target-cpu=native, AVX-512 host) 4/4
AVX2 --config .cargo/config-v3.toml 4/4
AVX without AVX2 --config .cargo/config-avx.toml, qemu-x86_64-static -cpu SandyBridge 4/4
scalar (also serves NEON and wasm) --config .cargo/config-v2.toml, qemu-x86_64-static -cpu Nehalem 4/4
nightly-simd cargo +nightly test --features nightly-simd 4/4

Other gates:

  • The full lib suite passes on the default build: 2653 passed, 0 failed.
  • The 22 U16x32 doc examples pass.
  • cargo fmt --check is clean, and facet-stride-guard.sh --diff origin/master exits 0.
  • cargo clippy --lib -- -D warnings is clean on the default, v3 and v2 builds.
  • On the AVX arm, clippy fails on master too, and none of its errors is in the new code.
  • Disable run: making the AVX2 cmpgt_mask compute >= turns the AVX2 parity run red.

Not done: no bench against scalar yet (acceptance criterion 4). The consumer PR in turbovec measures the kernel end to end.


Generated by Claude Code

U16x32 had a different method set on each backend, so code written against
ndarray::simd could not use zero(), the u16->f32 widening, the compare masks,
min/max or saturating arithmetic portably. Every backend (AVX-512, AVX2, AVX,
scalar, nightly) now carries the same surface: zero, simd_min/max,
saturating_add/sub, cmpeq_mask/cmpgt_mask, reduce_min/max, to_f32x16_lo/hi,
the bit operators, Not, SubAssign and Default, and the widening, shift,
multiply and pack methods on nightly. reduce_sum returns u16 everywhere.

The new parity test also caught two AVX-512 bugs: shl/shr returned 0 for any
count other than 1, 2, 4 or 8 (including 0), and pack_saturate_u8 saturated as
signed and interleaved its inputs per 128-bit lane. Both now match the scalar
reference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
…ckboard

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: a8374ad8-6b2c-444d-abb1-011c54ec19af
  • 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 pushed a commit to AdaWorldAPI/turbovec that referenced this pull request Oct 29, 2026
Re-applies this fork's work on top of upstream 1.1.2, as one commit. The
backup branches backup/main-pre-upstream-sync-2026-10-29{,-b} keep the
original history.

search_multi_query_avx2 now uses only the ndarray::simd facade, 64 bytes
wide: one U8x64 load covers two adjacent byte groups (codes and LUT),
shuffle_bytes does the lookups, U16x32::from_u8x64_lo/hi widen into the
u16 accumulators (replacing the SUB trick), U16x32::to_f32x16_lo/hi +
F32x16::mul_add flush each FLUSH_EVERY batch, and F32x16::simd_gt prunes
against the heap minimum. Scores are bit-identical to the AVX-512 kernel.

Also:
- FORCE_AVX2_PATH forces the AVX2 kernel on AVX-512 hosts (tests and the
  bench-internals feature); avx2_ndarray_simd_parity compares it with the
  default kernel bit for bit, including a two-batch dim and a partial
  block.
- examples/kernel_speed.rs times the native, AVX2 and scalar paths.
- ndarray fork dependency (x86_64 only, no BLAS), currently on the
  claude/u16x32-facade-gaps branch until AdaWorldAPI/ndarray#385 merges.
- Toolchain 1.99.0, which the ndarray fork requires: rust-toolchain.toml,
  rust-version in both crates, and the CI clippy pin, with the two lint
  classes 1.99 adds to existing code allow-listed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review October 29, 2026 12:09
@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-29T12:11:33.105464Z ca7dbab 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.

@coderabbitai

coderabbitai Bot commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Add Carrot credits or activate Agent usage billing to use Autofix

@AdaWorldAPI
AdaWorldAPI merged commit f8eb95e into master Oct 29, 2026
31 checks passed
AdaWorldAPI pushed a commit to AdaWorldAPI/turbovec that referenced this pull request Oct 29, 2026
… is merged

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMMjKnig6MXTd41Lf2v
AdaWorldAPI pushed a commit that referenced this pull request Oct 29, 2026
…1848

Adds section R2. Rows moved: H1 partly closed by #385 (first U16x32
parity found two AVX-512 bugs); H3 reframed (count_ones lowers to the
same vpshufb nibble table on AVX2, so a rewrite would only hide it);
AVX2 mask_blend set as R2 burn-down item #1 by measurement. New dots
N10-N15: the two 9-bit contracts agree across repos; recall and cost
were measured on different layouts; the AVX2 masked dot hides
compiler-emitted shuffles the source guard cannot see; W4's ndarray
half shipped in #386; the u8->i8 view and R3 are one ruling; NEON and
wasm lib tests are not in CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XAqKMPh2Seh2WLccCksAdY
AdaWorldAPI added a commit that referenced this pull request Oct 29, 2026
Synergy map revision after #385–#393 and lance-graph #1847–#1848
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