Sub-issue of #1135. The answer to "does eager need its own reuse mechanism": yes, and it already exists — the Scope split. Eager lifetimes follow call structure, so ForwardScope.reset() at step boundaries replaces graph liveness analysis. What's missing is purely wiring: ExecutionContext.memoryScope (@ExperimentalMemoryApi, defaults Scope.Ambient) has zero readers today.
Scope
- New
sk.ainet.lang.tensor.data.StorageFloatTensorData: offset-aware dense FP32 TensorData over Storage.Heap (reads via floats + arrayOffset, exposes view). Deliberately does not implement FloatArrayTensorData — slab slices have nonzero arrayOffset and the ops fast paths assume offset 0.
ExecutionContext.zeros/full/fromFloatArray: when memoryScope !== Scope.Ambient and the dtype is dense FP32, allocate via memoryScope.allocateFloats(...) and wrap; Ambient short-circuits to today's factory path (token-identical default).
- New
ScopedExecutionContext decorator + forwardScope(slabFloats) { … } convenience that resets/closes.
- KDoc on
ExecutionContext.scratch vs memoryScope: ScratchPool stays as the intra-kernel untyped workspace; memoryScope governs inter-op activation lifetime. Kept separate on purpose.
Acceptance
- New
ExecutionContextScopeTest: Ambient identity; slab draw (usedFloats > 0), second step allocates zero new slab bytes; reset() → StorageClosedException on stale read; retain() escape hatch; an op applied to a slab-backed tensor compared numerically against the Ambient result (guards the offset-0 fast-path trap).
- Full pr-gate green (strictly opt-in change).
Follow-up (separate issue): routing eager op outputs through ctx.memoryScope — DefaultCpuOps' 34 raw FloatArray sites and generation-loop adoption.
Sub-issue of #1135. The answer to "does eager need its own reuse mechanism": yes, and it already exists — the Scope split. Eager lifetimes follow call structure, so
ForwardScope.reset()at step boundaries replaces graph liveness analysis. What's missing is purely wiring:ExecutionContext.memoryScope(@ExperimentalMemoryApi, defaultsScope.Ambient) has zero readers today.Scope
sk.ainet.lang.tensor.data.StorageFloatTensorData: offset-aware dense FP32TensorDataoverStorage.Heap(reads viafloats+arrayOffset, exposesview). Deliberately does not implementFloatArrayTensorData— slab slices have nonzeroarrayOffsetand the ops fast paths assume offset 0.ExecutionContext.zeros/full/fromFloatArray: whenmemoryScope !== Scope.Ambientand the dtype is dense FP32, allocate viamemoryScope.allocateFloats(...)and wrap; Ambient short-circuits to today's factory path (token-identical default).ScopedExecutionContextdecorator +forwardScope(slabFloats) { … }convenience that resets/closes.ExecutionContext.scratchvsmemoryScope: ScratchPool stays as the intra-kernel untyped workspace;memoryScopegoverns inter-op activation lifetime. Kept separate on purpose.Acceptance
ExecutionContextScopeTest: Ambient identity; slab draw (usedFloats > 0), second step allocates zero new slab bytes;reset()→StorageClosedExceptionon stale read;retain()escape hatch; an op applied to a slab-backed tensor compared numerically against the Ambient result (guards the offset-0 fast-path trap).Follow-up (separate issue): routing eager op outputs through
ctx.memoryScope— DefaultCpuOps' 34 rawFloatArraysites and generation-loop adoption.