simd: exchange<const G> on U32x16 — the transpose surface, all six backends - #270
Conversation
…ckends
blake3-on-ndarray-simd.md identified the gap and prescribed the shape: "what
is missing is a method surface on U32x16, implemented as the scalar index
loops LLVM already handles... it wants to be one const-generic operation
rather than five named ones." This is that operation.
exchange::<G>(lo, hi) -> (lo', hi')
pairs lane c with lane c ^ G. G = 1 is the 32-bit unpack, G = 2 the 64-bit
unpack, G = 4 the 128-bit lane exchange, G = 8 the 256-bit half exchange that
a 512-bit lane needs and for which no AVX2 intrinsic exists. Four stages over
it -- pairing row r with row r | G -- compose a complete 16x16 transpose.
Why const-generic rather than five named methods: G must be compile-time
constant so every shuffle pattern is fixed. That is the precondition for LLVM
to select a shuffle instead of an indexed copy, and it is the difference the
oracle measured between the two spellings of the same function:
transpose_16x16_u32 (monolithic index loop) 0 packed
1088 B of stack, a 256-iteration scalar copy
transpose_16x16_composed (four butterfly stages) 79 packed / 0 scalar
19 real shuffles: vinserti128, vpshufd, vpblendd,
vpunpcklqdq, vpermq, vunpcklpd, vpermpd
No intrinsic override is earned. Under the entry criterion in
simd-one-spec-design.md an override needs a probe showing the generic form
fails; it does not fail here. Bodies are scalar index loops, zero unsafe, zero
core::arch, on every backend.
Semantics note: this is NOT interleave_*/concat_*, which were added in #267
and mirror x86's per-128-bit-lane unpack. exchange pairs across the whole
vector. Neither substitutes for the other, and both are now tested.
Two tests. u32x16_exchange_stages_compose_a_transpose composes the four stages
and checks the result against the expected transpose -- the same control the
oracle driver applies to its probe-local twin, brought into the suite so the
library method is verified rather than its copy. A packed-but-wrong network
passes a codegen histogram and fails this. u32x16_exchange_is_lane_exact_per_
granularity pins each G independently so one bad stage cannot hide inside the
composition.
34 simd tests pass on 1.95.0.
Not claimed: throughput. The oracle measures instruction class, not speed --
"emits packed shuffles" is not "beats the intrinsic backend", and no benchmark
against rust_avx2.rs has been run on either side.
Claude-Session: https://claude.ai/code/session_01VdfbkUCBbtZhy3yjSfCDHp
|
Warning Review limit reached
Next review available in: 53 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
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. Comment |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_98aa19a6-5c06-48a3-a3e4-313cdb529c02) |
format/stable failed on the previous commit. No logic change.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c8dc217aee
ℹ️ 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".
| nl[c] = if c & G == 0 { l[c] } else { h[c ^ G] }; | ||
| nh[c] = if c & G != 0 { h[c] } else { l[c ^ G] }; |
There was a problem hiding this comment.
Reject unsupported exchange granularities
Because this public const-generic API does not constrain G to the documented values 1, 2, 4, and 8, calls such as exchange::<3> silently duplicate and discard lanes rather than performing an exchange, while exchange::<16> indexes beyond the 16-lane arrays and panics. The tests exercise only valid powers of two, so add a const/runtime assertion for the supported set to this implementation and the five replicated backend implementations.
Useful? React with 👍 / 👎.
blake3-on-ndarray-simd.mdidentified this gap and prescribed the shape:This is that operation.
Grust_avx2.rs_mm256_unpack{lo,hi}_epi32(8 sites)_mm256_unpack{lo,hi}_epi64(8 sites)_mm256_permute2x128_si256(2 sites)Four stages over it — pairing row
rwith rowr | G— compose a complete 16×16 transpose.Why const-generic, not five named methods
Gmust be compile-time constant so every shuffle pattern is fixed. That is the precondition for LLVM to select a shuffle instead of an indexed copy, and it is the entire difference the oracle measured between two spellings of the same function:transpose_16x16_u32— monolithic index looptranspose_16x16_composed— four butterfly stagesvinserti128×7,vpshufd×3,vpblendd×3,vpunpcklqdq×2,vpermq×2,vunpcklpd,vpermpdNo intrinsic override is earned
Under the entry criterion in
simd-one-spec-design.md, an override needs a probe showing the generic form fails. It does not fail here — the primitive vectorizes, and so does a composition of primitives. Only the monolithic spelling fails, and that is a spelling, not a capability.Bodies are scalar index loops. Zero
unsafe, zerocore::arch, on all six backends (avx512 / avx2 / neon / wasm / scalar / nightly).Semantics: this is not
interleave_*/concat_*Those were added in #267 and mirror x86's per-128-bit-lane
unpack.exchangepairs across the whole vector. They are different permutations and neither substitutes for the other. Both are now tested.Tests
u32x16_exchange_stages_compose_a_transpose— composes the four stages and checks against the expected transpose. This is the control the oracle's driver applies to its probe-local twin, brought into the suite so the library method is verified rather than a copy of it. A packed-but-wrong shuffle network passes a codegen histogram and fails this.u32x16_exchange_is_lane_exact_per_granularity— pins eachGindependently, so one bad stage cannot hide inside the composition.34 simd tests pass on 1.95.0.
Not claimed
Throughput. The oracle measures instruction class, not speed. Per the knowledge doc: "'Emits packed shuffles' is not 'is faster than the intrinsic backend' — that needs a bench against
rust_avx2.rs, which has not been run." No benchmark has been run on either side, and this PR does not change that.Also unmeasured on aarch64 and wasm — the oracle is per-triple and only
baseline-x86_64-v3.tomlexists. The lane-exact test covers those backends for correctness; nothing covers their codegen.Relationship to the other open PRs
Independent of #269 (drops the blake3 dependency) and
AdaWorldAPI/BLAKE3#1(removes the C backends). This is the primitive both of those eventually want — BLAKE3#1'sndarray_simdbackend currently does its message transpose as a scalar gather, which is the 0-packed spelling above, and should be rewritten overexchangeonce this lands.Generated by Claude Code