Repository navigation
perf(native-cpu): AVX2/FMA lane for the dense FP32 x·Wᵀ kernel on x86-64 - #1442
Merged
michalharakal merged 2 commits intoOct 24, 2026
Merged
michalharakal merged 2 commits into
michalharakal merged 2 commits into
Conversation
skainet_fp32_matmul_wt had SIMD bodies for wasm and NEON only; the x86-64
library is built for baseline x86-64, so the kernel behind the JVM's
resident dense weights ran an SSE2 loop and re-read the weight once per
activation row. Hosts with AVX2 and FMA now select, through a cached cpuid
probe (skainet_cpu_has_avx2_fma), a per-function target("avx2,fma") lane:
eight-lane FMA chains over a tile of four weight rows by two activation
rows, the weight streamed from memory once per call.
Every tile variant reduces an output in the same order, so threaded,
sliced and single calls stay bit-identical to each other; the header and
KDoc now describe that instead of a bit-identity to transpose-then-GEMM
that the NEON and wasm lanes never had.
Measured single call on a 6-core host, m=63 n=1024 k=1024: 29.6 → 131.7
GFLOP/s; decode m=1: 28.5 → 71.7; one-thread n=256: 9.0 → 56.5.
michalharakal
added a commit
that referenced
this pull request
Oct 24, 2026
…nd on AVX2 CHANGELOG [0.62.2] - 2026-10-24: the schedule-driven split of the dense FP32 x·Wᵀ product (#1441), the runtime-dispatched AVX2/FMA lane for the same kernel on x86-64 (#1442), the CoroutineSchedule.forRange empty-range fix, the added-token index in the text front-end (#1443), and the dependabot bumps. The #1441 entry, dropped from Unreleased by a develop merge, is restored. VERSION_NAME, CITATION, Antora version, README bom coordinate, What's New and contributors, the Android tutorial coordinates; regenerated operator and kernel-support matrices (version stamp).
2 of 3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
skainet_fp32_matmul_wt— the kernel behind the JVM's resident dense FP32 weights (SKEEP-007) — gains an x86-64 AVX2/FMA lane, selected at runtime. Every other target and every x86 host without AVX2 runs the previous code unchanged.Why
The native library is built for baseline x86-64 (no
-march, it has to run on any x86-64), so on x86 the kernel took its portable loop: SSE2 code, one accumulator, and the weight re-read once per activation row. The wasm and NEON builds have had SIMD bodies since SKEEP-007; x86 never did. In the LayaMoE encoder pass this kernel is two thirds of the time (#1441 describes the profile), and it is the FP32 product every JVMLinearresolves to onceskainet-backend-native-cpuis on the classpath.How
skainet_cpu_features.{h,c}:skainet_cpu_has_avx2_fma()— GCC/Clang__builtin_cpu_supports("avx2") && …("fma"), MSVCcpuidleaf 1 (FMA, AVX, OSXSAVE) + leaf 7 (AVX2) +xgetbvYMM state; cached;0elsewhere. Internal header, not part of the cinterop surface.fp32_matmul_wt.c:skainet_fp32_wt_rows_avx2, compiled per function with__attribute__((target("avx2,fma")))(MSVC emits VEX intrinsics without/arch), the same per-function dispatch the Apple arm64 dotprod kernels use. Tile: four weight rows × two activation rows, eight__m256FMA chains fed by six loads per step, so the four weight rows stay in the core's cache while the activation rows stream past — the weight is read from memory once per call instead of once per activation row. Tails: 4 × 1 and single-row.k & ~7, one fixed horizontal sum, the scalar tail in order — so an output's bits do not depend on which tile, thread or row slice computes it. The row pool's bit-identity promise holds, and so does the slice-independence that perf(backend-cpu): dense FP32 x·Wᵀ runs under the context schedule (SKEEP-005) #1441's schedule split relies on. The old header claim "bit-identical to transpose-then-GEMM" was already untrue for the wasm and NEON lanes; the header, the KDoc and the CHANGELOG now say what holds.skainet_run_rowsand the Kotlin binding are untouched. No Kotlin API change.Measured
i7-9750H (6 cores, 12 threads), GCC 13.3, one native call per shape through the segment face (the C row pool is the only threading), median of five; baseline = the develop kernel in the same harness:
End to end, the LayaMoE example (SKaiNET-examples, ModernBERT-large encoder, FP32 lane,
cli bench --index 0: three questions plus the router, 370 tokens) on the same host: 13.0 s per request on 0.62.1, 9.3 s with #1441 alone, 6.4 s with #1441 and this lane together (3 reps 6376/6477/6416 ms). Routing and every answer are the same; the largest change in any probability is 9.7e-07.Tests
NativeFp32MatmulWtX86LaneTest(skainet-backend-native-cpujvmTest): 80 shapes over every tile edge (k∉ 8ℤ,n∉ 4ℤ,m∈ {1, 2, 3, 63}) against a double-accumulated reference; a full call against two calls split at an unaligned column (and against row-at-a-time calls) bit for bit; the bench above behind-Dskainet.runBench=true. On a host without AVX2 the same tests exercise the portable lane.NativeFp32MatmulWtKernelTest,JvmDirectWeightTransposedTest, full:skainet-backend-native-cpu:jvmTest,:skainet-backend-cpu:jvmTest, bothapiChecks green locally.native-cpu-multiarchwindows-x86_64 leg is the gate for it.CHANGELOG
## [Unreleased]→ Changed.