feat(io): load a weight in kernel feed order, and have it say so - #1127
Merged
Merged
Conversation
Closes #1120. Follows #1124/#1126. A weight can now be permuted into the order the packed matmul kernels read at load time, and the resulting TensorData declares BlockOrder.INPUT_BLOCK_MAJOR rather than pretending to be canonical. Every reader is then right about the same bytes: the kernels address them in feed order deliberately, while toFloatArray() and view decoding walk the logical grid and fetch each block from where that order put it. The payoff is that #1096's O(bytes) per-weight relayout stops running at all for such a weight. DefaultCpuOpsJvm relabels the shape over the same array instead — asserted structurally with assertSame on the byte array, since a relayout would necessarily produce a different one, and timing proves nothing. Two things that had been conflated are now separate. relayoutPackedWeightForKernels still produces bytes labelled [in, out] for the JVM kernels; that is a private artifact whose shape and blocks disagree, which is exactly why #1126 found it undecodable and why it keeps no declared order. A loader-produced feed-order weight is a different thing: it keeps its [out, in] shape and describes itself. The permutation reuses TensorView.prepack(INPUT_BLOCK_MAJOR), which already owned it and already emits the conversion on the trace — so #1117 reports the prepack without new code, and a test counts exactly one per weight and none for a weight kept as stored. KERNEL_FEED requires WeightShapeOrientation.OUT_IN, and says so: which block is "block b of output row o" has no answer while the tensor is still labelled in the file's ne order, so refusing beats permuting against a meaningless grid. #1115's test asserting the old refusal is rewritten rather than deleted — it now pins the constraint that remains. Gate: scripts/pr-gate.sh — all legs passed. One earlier run failed SlicingTest.testPerformanceAccessPatterns on ChromeHeadless, an unrelated dense-slicing test that passes on its own; not touched here. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
📖 Documentation Preview The documentation has been built successfully for this PR. Generated Files:
Artifacts:
This comment will be updated automatically when the PR is updated. |
6 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.
Closes #1120. Follows #1124 / #1126.
A weight can now be permuted into the order the packed matmul kernels read at load time, and the resulting
TensorDatadeclaresBlockOrder.INPUT_BLOCK_MAJORinstead of pretending to be canonical.That is the whole idea: every reader is right about the same bytes. The kernels address them in feed order deliberately;
toFloatArray()and view decoding walk the logical grid and fetch each block from where that order put it. Before, feed-order bytes in a type claiming to be canonical decoded to plausible garbage — #1124, and #973/#968 before it.The payoff
#1096's O(bytes) per-weight relayout stops running at all for such a weight.
DefaultCpuOpsJvmrelabels the shape over the same array instead.Asserted structurally, not by timing:
A relayout would necessarily have produced a different array, so this is the claim rather than a proxy for it.
Two things that had been conflated
relayoutPackedWeightForKernels[in, out][out, in]INPUT_BLOCK_MAJORThe first is a private artifact for the JVM kernels, which address
packedDatain feed order themselves. #1126 established that it has no honest block order to declare — marking it was the first fix I tried there and it produced zeros. It is left exactly as it was. The second is the new, self-describing thing, and only it flows through views.Reusing what already existed
The permutation is
TensorView.prepack(INPUT_BLOCK_MAJOR), which already owned it — and already emits the conversion on the trace, so #1117 reports the prepack with no new code. A test counts exactly one per weight, and none for a weight kept as stored.One constraint, stated rather than assumed
KERNEL_FEEDrequiresWeightShapeOrientation.OUT_IN. Which block is "block b of output row o" has no answer while the tensor is still labelled in the file'sneorder, so refusing beats permuting against a meaningless grid.Tests
KernelFeedOrderTest(5): the order is declared and the shape kept; feed-order bytes differ from canonical yet decode to the same matrix — with the difference asserted, so a vacuous fixture cannot pass; the product agrees either way; the permutation happens once and is traced; theOUT_INconstraint refuses.FeedOrderWeightNoCopyTest(2,commonTestso it runs on native too): the no-copy claim, and a hand-permuted feed-order weight agreeing with the canonical weight it came from.All fixtures are three blocks per row — at one block per row the two orders coincide and every assertion here would hold vacuously (#968).
#1115's test asserting the old refusal is rewritten, not deleted: it now pins the constraint that remains.
Gate
scripts/pr-gate.sh— all legs passed. An earlier run failedSlicingTest.testPerformanceAccessPatternson ChromeHeadless — an unrelated dense-slicing test that passes on its own, and the same class of load-sensitive assertion as the one #1107 fixed. Not touched here.🤖 Generated with Claude Code