Repository navigation
perf(backend-cpu): dense FP32 products through the platform BLAS, on by default on macOS - #1449
Merged
michalharakal merged 3 commits intoOct 25, 2026
Conversation
…by default on macOS JvmBlas never reached a BLAS: it looked the Accelerate framework up by its bare name, which dlopen cannot resolve, and passed heap arrays to a downcall, which JDK 21 rejects, so skainet.cpu.blas.enabled was a no-op. It now resolves cblas_sgemm from skainet.cpu.blas.library (path or soname), the Accelerate framework by path on macOS, or the common Linux sonames, and has an off-heap face with both transposes. Three products use it when blasEnabled and above a small-work floor: A·B on heap arrays (staged, offsets allowed), A·B on two segment-backed operands (in place) and the direct x·Wᵀ on a resident dense weight (sgemm with the weight transposed, no copy of it), ahead of the schedule split since the BLAS threads itself. On macOS the default is now on — Accelerate's SGEMM runs on the matrix units of Apple silicon, several times the NEON kernel's throughput; -Dskainet.cpu.blas.enabled=false restores the bundled kernels. Elsewhere the default stays off. JvmBlasTest runs wherever a cblas_sgemm resolves and reports itself skipped otherwise; the test task forwards the BLAS properties so a suite can be run against a BLAS. Verified here against the reference BLAS on Linux (same cblas ABI); the Accelerate path itself needs a Mac.
…e-sgemm # Conflicts: # CHANGELOG.md
aharakal
approved these changes
Oct 25, 2026
Merged
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.
Summary
Dense FP32 products on the JVM can go to the platform BLAS, and on macOS they do by default: Accelerate's SGEMM runs on the matrix units of Apple silicon and threads itself, several times what any NEON kernel reaches per core. Everywhere else the default stays off and nothing changes.
Motivation: the x86-64 work (#1442, #1445) left the arm64 FP32 kernels where x86 was — an untiled 4-lane loop for
A · Band a single latency-bound chain forx · Wᵀ. On a Mac the fastest fix is not a NEON tile but the BLAS the platform ships; the NEON tiles (for Android, iOS and Linux arm64) follow in a separate PR.What was wrong
JvmBlasnever reached a BLAS. It looked the framework up by the bare name"Accelerate", whichdlopencannot resolve (a framework lives under/System/Library/Frameworks), and passedMemorySegment.ofArrayheap segments to a downcall, which JDK 21 rejects. Both failures were swallowed byrunCatching, soskainet.cpu.blas.enabled=truewas a silent no-op.What changes (
skainet-backend-cpu, JVM only)JvmBlasrewritten: resolvescblas_sgemmfrom-Dskainet.cpu.blas.library/SKAINET_CPU_BLAS_LIBRARY(path or soname), then the Accelerate framework by path on macOS, then the common Linux sonames (libopenblas.so.0,libblas.so.3,libcblas.so.3);libraryDescriptionsays which one. Two faces:sgemm(transA, transB, …)over off-heap segments with byte offsets and leading dimensions, and a heap face forFp32MatmulKernel-shaped operands that stages its arrays (as the native heap kernel does) and writes only them × nblock.blasEnabledand above a small-work floor (2¹⁸ multiply-adds):A · Bon heap arrays, now with offsets (slab windows) — replaces the dead block that required offset 0 and 67 M multiply-adds;A · Bon two segment-backed operands, read in place;x · Wᵀon a resident dense weight:sgemmwith B transposed andldb = kreads the[out, in]weight where it lies, the activation is staged (m × k) — taken before the schedule split, since Accelerate threads itself.JvmCpuBackendConfig.blasEnableddefaults to true on macOS (os.name), false elsewhere; the property and env override either way. Documented inperf/jvm-cpu.adoc.skainet.cpu.blas.enabled/.libraryto the forked JVM so a suite can be run against a BLAS.### Changed.Numerics: a BLAS orders its sums as it likes, so results move within rounding against the bundled kernels — the same category as the lane differences already accepted between x86, NEON and wasm. Within one host the choice is fixed per process.
Tests
New
JvmBlasTest(runs wherever acblas_sgemmresolves, reports itself skipped otherwise):63 × 1021 × 1023andk = 0, against a double reference;Run here on Linux against the reference BLAS (
libblas.so.3, the same cblas ABI Accelerate exports):skainet-backend-cpu:jvmTestwith-Dskainet.cpu.blas.enabled=true -Dskainet.cpu.blas.library=/lib/x86_64-linux-gnu/libblas.so.3— 495 tests, 0 failures (the macOS proxy: every product in the suite went through BLAS);skainet-backend-cpu:jvmTestwith defaults — 495 tests, 0 failures;apiCheckgreen (no public API change).Not verified here
No Apple hardware in this session and the macOS CI leg does not run
jvmTest. The Accelerate path (framework resolution by path, and the speed) needs a run on a Mac:./gradlew :skainet-backends:skainet-backend-cpu:jvmTest --tests '*JvmBlasTest*'should print[blas] /System/Library/Frameworks/Accelerate.framework/Accelerate, and-Dskainet.cpu.blas.enabled=falserestores the bundled kernels for an A/B.