feat(backend-cpu): route the generic matmul path through the kernel registry (SKEEP-003 P3, S1.7b) - #1071
Merged
Conversation
…egistry (SKEEP-003 P3) Milestone M1 (#1002), PRD M1-F5 / M1-A4. The fast paths are untouched: the packed-quant kernels and the FP32 2D×2D path run exactly as before. What changes is the *fallback* — the path where #993 crashed: - DefaultCpuOps.matmul now tries dispatchMatmulViaRegistry() before matmulGeneric(). It builds views from both operands (TensorData.view, #1068/#1069), transposes the weight as a view, normalises the activation once (rank-1 -> [1, k], batched -> [rows, k]) and hands the key to KernelDispatch, which selects a registered kernel or the decoding reference kernel. A packed weight is therefore decoded, never read as a raw byte, whatever the activation's rank or subtype. - The result's shape is restored from the normalisation ([k] x [k, n] -> [n], batched dims preserved), so callers see no change. - DispatchMode (backend-api, expect/actual): skainet.dispatch.registry =false forces the legacy per-element fallback, and overrideEnabled lets a test pin either path. The legacy code stays until the migration is complete, then it goes. - RegistryMatmulDispatchTest: the #993 repro (rank-1 decode step against a Q8_0 weight) is correct and finite; registry and legacy paths agree elementwise on the same inputs; batched activations flatten and reshape; a Q4_K weight decodes correctly. 4/4. Closes #1028 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
Local gate Targeted: |
Contributor
Author
|
Fast paths unchanged — targeted JMH on this branch (c570a34) vs
worst slowdown: +0.2% (positive = slower after M0) Worst movement +0.2 %, everything inside the error bars — as expected: the packed-quant kernels and the FP32 2D×2D path are untouched by this slice, only the generic fallback was rerouted. |
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
SKEEP-003 slice S1.7b (milestone M1 #1002, PRD M1-F5 / M1-A4): the generic matmul path in
DefaultCpuOps(commonMain) now goes through the kernel registry — the first slice where behaviour actually moves, kept deliberately narrow.What does not change: the packed-quant fast paths (
chooseQuantizedMatmulHeap→ the registered scalar/vector kernels) and the FP32 2D×2D fast path run exactly as before. No hot path is touched, so the benchmarks stay where they were.What changes: the fallback — the path where #993 crashed.
matmulnow triesdispatchMatmulViaRegistry()beforematmulGeneric():TensorViews (TensorData.view, from feat(memory): TensorData exposes a zero-copy TensorView — dense, narrow-float and MemorySegment façades (SKEEP-003 P2, S1.4a) #1068/feat(memory): packed TensorData façades — every GGML block format and ternary expose a zero-copy, bit-identical TensorView (SKEEP-003 P2, S1.4b) #1069);[1, k], batched →[rows, k](zero-copy views, §5.1);KernelDispatchselects a registered kernel or the decodingReferenceMatmulKernel.So a packed weight is decoded, never read as a raw byte, whatever the activation's rank or
TensorDatasubtype — #993 (rank-1 decode step) and #991 (unexpected activation subtype) stop being crash classes and become ordinary dispatch. The output shape is restored from the normalisation ([k] × [k, n] → [n], batched dims preserved), so callers see no difference.DispatchMode(backend-api, expect/actual over the platforms):-Dskainet.dispatch.registry=falseforces the legacy per-element fallback, andoverrideEnabledlets a test pin either path. The legacy code stays until the migration is complete (#1029 does the JVM packs), then it goes.Evidence —
RegistryMatmulDispatchTest(4/4):DispatchMode);[2, 3, 32]activations flatten and reshape to[2, 3, n];Plus the golden parity tests pass unchanged — the guard that matters for a behaviour-moving slice — along with the rest of the gate.
Test plan
Full local gate (
scripts/pr-gate.sh, JDK 25) — all legs passed, including the packed-encoding golden parity tests; results in the first comment.Closes #1028
🤖 Generated with Claude Code