Add an ndarray-blas_gemm-backed AVX-512 f32 GEMM kernel candidate - #4
Conversation
…yfill Adds a [patch.crates-io] entry pointing ndarray at the AdaWorldAPI fork (git, master branch, so it resolves in any checkout including CI). The fork is API-compatible (same 0.17.2) but carries an additional simd/hpc module (crate::simd::F32x16, AVX-512/AVX2/NEON/scalar tier dispatch) that tract's own linalg kernels can be wired to later. This patch alone changes no behavior; tract's element-wise/reduce kernels still use their existing intrinsics path until a follow-up adds a parallel, non-overlapping kernel registration that calls into it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012wrzeZAdwGYTCKoxamwQht
CI was failing across the whole 1.91 matrix because the ndarray fork patched in by the previous commit requires rustc 1.97 (its rust-toolchain.toml is permanently pinned there for AdaWorldAPI-stack alignment). CI derives its tested toolchain straight from Cargo.toml's rust-version, so bumping that one field re-targets every job at once; updated the README rustc badge to match per the comment above that field. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012wrzeZAdwGYTCKoxamwQht
Both api/rs/deny.toml and cli/deny.toml gate git dependencies with an allow-git list that only permitted rustformers/llm; the ndarray patch resolves ndarray/fractal/p64 from AdaWorldAPI/ndarray, which the cargo-deny CI job would otherwise reject. Also trimmed the patch.crates-io comment to the current constraint (why the git pin exists) instead of narrating a follow-up change that hasn't happened. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012wrzeZAdwGYTCKoxamwQht
…nal 16x8 candidate avx512_mmm_f32_16x8 was the only f32 GEMM tile with no ndarray-backed sibling, so this adds ndarray_avx512_mmm_f32_16x8: same tile geometry, same fused-op interpreter as the generic reference kernel, but the AddMatMul accumulation calls ndarray::simd::BlasLevel3::blas_gemm (the AdaWorldAPI fork's canonical consumer-facing re-export, not hpc::blas_level3 directly) instead of a hand-written inner-product loop. Registered purely additively through the existing MMMRustKernel! machinery; no existing kernel, tier, or preference is touched, and a new test pins that default dispatch still picks the asm kernel. Requires a companion ndarray-fork change (adding the blas_level3 re-export to simd.rs, made in the local checkout) that is not yet pushed upstream, so this currently only builds against a local path-patched ndarray, not the git-pinned fork. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012wrzeZAdwGYTCKoxamwQht
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_0d507683-0744-4108-814e-79f7a7056b08) |
|
CI red on The git-pinned Not re-running CI now since it will stay red until that PR merges to Generated by Claude Code |
…ilot-x9k2 # Conflicts: # linalg/Cargo.toml
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5e8250fe4d
ℹ️ 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".
|
Pushed a merge + fix (
Verified locally: Generated by Claude Code |
Same defensive fix as pilot v2 (PR #5): the symbolic-N fallback in core::ops::einsum::kernel_selection::strategize picks the largest-nr kernel per packing group, bypassing preferred/boost entirely. This kernel's nr=8 currently ties rather than exceeds the existing max (avx512_mmm_f32_16x12's nr=12), but relying on that ordering to hold is fragile and inconsistent with the "purely additive, no behavior change" guarantee this pilot claims. Register it via MMMRustKernel!'s lower-level form, which skips the inventory::submit! that makes a kernel discoverable by MmmDispatch::native() -- the kernel stays directly constructible for this pilot's own bench/tests, but is never selected automatically. Rewrote dispatch_stays_default to assert non-discoverability for both a concrete and a symbolic N. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012wrzeZAdwGYTCKoxamwQht
What
Additive-only pilot: a second
MatMatMulKerGEMM candidate,ndarray_avx512_mmm_f32_16x8, alongside the existing hand-tuned AVX-512 asm kernelavx512_mmm_f32_16x8(same 16x8 tile geometry, directly comparable). ItsAddMatMulfused-op step callsndarray::simd::BlasLevel3::blas_gemmfrom the AdaWorldAPI ndarray fork instead of a hand-written inner-product loop; every other fused op (bias, min/max, per-row/per-col, store) is the same scalar Rust the crate's own generic reference kernel (crate::generic::mmm::kernel) uses.Import goes through
ndarray::simd::BlasLevel3— the ndarray fork's canonical consumer-facing re-export — neverndarray::hpc::blas_level3directly, per that repo's ownCLAUDE.md("all SIMD fromndarray::simd"). This required a small companion change in the ndarray fork itself (src/simd.rsdid not yet re-exportblas_level3::{BlasLevel3, Side}), made locally in that checkout but not yet pushed upstream — so this PR currently only builds against a local path-patchedndarray, not the git-pinned fork this repo's[patch.crates-io]points at. The fork-side diff is onepub useline, in the same style as the existinghpc::amx_matmul/hpc::bf16_tile_gemmre-export blocks in that file.Shape/dtype piloted
f32 GEMM, 16x8 tile (matching
avx512_mmm_f32_16x8'smr/nr), on a full matrix multiply (not a single tile call) —m=n=k∈{512,1024}— via a criterion bench (linalg/benches/ndarray_gemm.rs) that packs real operands withPackedFormat::prepare_oneand callskernel.run(m, n, ops), so the kernel's own panel-walking machinery loops the 16x8 tile many times, same as production dispatch would.Real benchmark numbers (this VM,
RUSTFLAGS="-C target-cpu=native",CARGO_PROFILE_*_DEBUG=0, release/bench profile, criterion, sample-size 20, Sapphire-Rapids-class Xeon confirmed avx512f/bw/cd/dq/vl/vnni/bf16/fp16)avx512_mmm_f32_16x8What this shows, plainly: the ndarray-backed candidate is ~5-10x slower than the existing hand-tuned asm kernel at both shapes measured. The dominant cost is architectural, not the underlying GEMM math: each 16x8xk tile call allocates and transposes a fresh
(16, k)buffer (the packed-A panel is k-major/MR-contiguous, so it must be copied into a contiguous(MR, k)layout before it can reachblas_gemmas a contiguous slice) and makes oneblas_gemmcall per tile, none of which the tight fused-op asm loop pays. This is not a case for swapping the default kernel — the numbers say the opposite, clearly.Additive-only, confirmed
.S.j2asm kernel, tier, or preference table entry is touched.MMMRustKernel!macro (same registration path the crate's own generic Rust kernels use) — nothing about kernel selection changes.x86_64::ndarray_gemm::dispatch_stays_default::adding_the_ndarray_candidate_does_not_change_default_pick, asserts the new kernel is suitable wherever avx512f is native but thatMmmDispatch::pickstill prefers the asm kernel — i.e. this PR provably changes nothing about default runtime dispatch.MatMatMulKercorrectness-test macros (test_mmm_kernel!→mmm_packed_packed_tests!/mmm_frame_tests!/mmm_kernel_fuse_tests!/mmm_store_test!, auto-generated by the sameMMMKernel!macro every other kernel uses) — no new test harness invented. 59 kernel-specific tests pass; the fulltract-linalglib suite (4439 tests) passes unchanged.cargo fmt --all -- --checkclean.cargo clippy -p tract-linalg --all-targets -- -D warnings: only 2 pre-existing errors remain (generic/reduce.rs::chunks_exact_mut,x86_64/mmm.rs::avx2_mmm_i32_8x8needless-borrow), confirmed present identically onmainbefore this change (clippy/toolchain drift, unrelated to this PR) — nothing new introduced by this diff.AMX
Not attempted — the f32/AVX-512 candidate above is the full deliverable for this pilot; there was no remaining budget for an AMX bf16 stretch candidate in this pass.
🍍
Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_012wrzeZAdwGYTCKoxamwQht
Generated by Claude Code