feat(memory): one view mechanism — Layout/TensorView subsumes sliced views, byte-range aliases and the packed transpose (SKEEP-003 P4, S2.1) - #1081
Merged
Conversation
…views, byte-range aliases and the packed transpose Closes #1034 (SKEEP-003 P4, S2.1, proposal §4.6, rule 5). Three unrelated ways of looking at someone else's bytes existed side by side: `SlicedTensorView` remapped indices, `BufferHandle.Aliased` named a byte range, and a packed transpose rewrapped the buffer. All three are a `TensorView` with a different `Layout` over the same `Storage`. - `Layout.step(axis, step)` / `TensorView.step` — the strided half of the old `Slice.Step` as a stride multiply, so narrow + step + squeeze cover every slice kind the index remapper handled. - `Views.kt`: `Tensor.view()` / `viewOrNull()` and `TensorView.slice(slices)` — the old `Slice` DSL replayed as layout arithmetic, returning the same view type `narrow`/`transpose`/ `unsqueeze`/`squeeze` return, so views compose instead of nesting wrappers. - `Layout` gains a **block axis**. A blocked layout used to assume its block-carrying axis was the last one, so transposing a packed view produced a view that decoded the wrong elements — the packed transpose was zero-copy but wrong. The block axis now travels with its extent through `transpose`/`unsqueeze`/`squeeze`, and `narrow`/`step` consult it, so a transposed packed weight decodes correctly and slices by whole blocks on whichever axis now carries them. - `sk.ainet.lang.tensor.TensorView`, `SlicedTensorView` and `BufferHandle.Aliased` are `@Deprecated` (WARNING) with the migration spelled out; nothing is removed and every caller keeps compiling. No `ReplaceWith` on the two types: the replacement is not type-parameterized, so an automatic fix would emit code that does not compile — the message says what to write instead. Internal users of the old mechanism carry a file-level `@Suppress("DEPRECATION")`. `DefaultCpuOps.transpose` still performs its O(bytes) block-grid permutation: the packed kernels read a weight as input-block-major whatever shape it declares (#968/#971), which is the contract #973 exists to write down. `PackedTransposeGoldenTest` pins both halves — the permuted bytes *are* the input-block-major reordering, and decoded as such they carry exactly the values the zero-copy view exposes — for all seven packed encodings, on JVM and Kotlin/Native. `OneViewMechanismTest` compares the new mechanism against the old ones directly: every slice kind (All/Range/At/Step, multi-axis, chained) agrees with `SlicedTensorView` on shape and values; a `Storage.slice` alias has the sharing, bounds and parent-tied semantics `BufferHandle.Aliased` documented; transpose and step are metadata only; and every derived view keeps the same `Storage`. No existing execution path changed — the deprecations are annotations and the new operations are additive — so `SlicingBenchmarks` measures the same code it did before. Gate: scripts/pr-gate.sh — all legs passed; scripts/pr-gate.sh --golden passed on JVM and linuxX64. 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. |
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 #1034 · Phase P4 · Milestone M2 · Proposal §4.6, §4.3 rule 5
The three mechanisms
Looking at someone else's bytes had three unrelated implementations:
SlicedTensorViewremapped indices through anIndexMapper,BufferHandle.Aliasednamed a byte range, and a packed transpose rewrapped the buffer. Rule 5 says there is one: a view is aTensorViewwith the sameStorageand a differentLayout.Layout.step(axis, step)adds the strided half of the oldSlice.Step(a stride multiply), sonarrow+step+squeezecover every slice kind the remapper handled.Views.ktgives DSL code the entry points:Tensor.view()/viewOrNull(), andTensorView.slice(slices)which replays the oldSlicevocabulary onto those operations — returning the same view type everything else returns, so views compose instead of nesting wrappers.Storage.slice. Already existed (Owner.Alias); this PR pins that it has the sharing, bounds and parent-tied semanticsBufferHandle.Aliaseddocumented, and deprecates the handle.The bug this found
A blocked
Layoutassumed the axis measured in blocks was the last one.TensorView.transpose()swapped the strides but left that assumption in place, so a transposed packed view decoded the wrong elements — zero-copy, and wrong. It was never asserted, because nothing transposed a packed view yet.Layoutnow carriesblockAxis, and it travels with its extent throughtranspose/unsqueeze/squeeze(withnarrow/stepconsulting it). A transposed packed weight decodes correctly and still slices by whole blocks, on whichever axis carries them now.What did not change
DefaultCpuOps.transposekeeps its O(bytes) block-grid permutation. The packed matmul kernels read a weight as input-block-major regardless of its declared shape — the bare shape swap that silently read garbage is #968/#971, and the contract is what #973 exists to write down. Until that lands, a zero-copy view cannot feed those kernels.PackedTransposeGoldenTestpins both halves for all seven packed encodings, on JVM and Kotlin/Native: the permuted bytes are the input-block-major reordering of the original blocks, and decoded as such they carry exactly the values the zero-copy view exposes. The two paths describe the same matrix by different addressing — stated as an assertion rather than left as folklore.Acceptance
OneViewMechanismTest(8 cases): every slice kind —All,Range,At,Step, multi-axis, chained — agrees withSlicedTensorViewon shape and values;slice()equals the hand-composednarrow/step/squeeze; aStorage.slicealias shares memory, is bounds-checked and declaresOwner.Alias; transpose and step are metadata only (sameStorageId, different strides); andview()is documented as a fresh handle per call over the same array.PackedTransposeGoldenTest(8 cases): shape, zero-copy (sameStorageId), element-wise transposition, the block-major contract above, block-aligned narrowing of a transposed view, and seven newtranspose/*goldens.Deprecations
sk.ainet.lang.tensor.TensorView,SlicedTensorViewandBufferHandle.Aliasedare@Deprecatedat WARNING level. Nothing is removed; every caller still compiles, and the files that are the old mechanism carry a file-level@Suppress("DEPRECATION")so the build stays quiet.No
ReplaceWithon the two types, deliberately: the replacement is not type-parameterized, so an IDE fix onTensorView<T, V>would producesk.ainet.lang.memory.TensorView<T, V>, which does not compile. The message says what to write instead.Gate
scripts/pr-gate.sh— all legs passed:jvmTest·apiCheck·verifyNpmPins jsTest wasmJsTest wasmWasiTest·linuxX64Test·assemble·:skainet-test:skainet-test-java:test.scripts/pr-gate.sh --golden— passed on JVM and linuxX64.No existing execution path changed — the deprecations are annotations, and the new operations are additive — so
SlicingBenchmarksmeasures the same code it measured before.Keeps develop green by
Deprecation over removal, and the only behavioural change is a packed-view transpose that was wrong and is now right. One API note for the dump:
Layoutgained ablockAxisconstructor parameter, which changes its constructor signature.Layoutis@ExperimentalMemoryApi, introduced in M1 and used only by the memory package, so this is a source-compatible addition with no downstream callers to break.🤖 Generated with Claude Code