feat(memory): Scope — Model / Forward / Ambient lifetimes; recycled Forward slab with reset() and retain() (SKEEP-003 P2, S1.2) - #1065
Merged
Conversation
…orward slab with reset() and retain() (SKEEP-003 P2) Milestone M1 (#1002), PRD M1-F3 / M1-F4. SKEEP-003 §4.5: weights and activations must not share a lifetime — the two historical arena failures (a model-lifetime arena pinning tens of GB; per-call arenas leaking per matmul) were violations of exactly this split. - sk.ainet.lang.memory.Scope (sealed): kind, liveBytes, sink, allocate(), allocateFloats(). Scope.Ambient is the GC-managed default: untracked, close() is a no-op, notebook code is unchanged. - ModelScope: tracks every storage it allocates, maps (mapFile) or adopts and closes them all exactly once — weights unmapped, off-heap freed — so Model.load(...).use { } is deterministic. - ForwardScope(slabFloats): one pre-sized heap slab (the Phase-2 spike #1016 says heap activations by default on the JVM), bump-allocated as zero-copy slab views; reset() closes the views handed out, frees overflow and rewinds the offset, so steady-state decode allocates zero slab bytes (asserted in the test: one Allocation event for the slab and none per step); usedFloats / peakFloats / overflowBytes / steps feed the planner and the plan-vs-actual check; retain(storage, to) is the one sanctioned escape (a copy), everything else becomes a StorageClosedException after reset. - ExecutionContext.memoryScope default member → Scope.Ambient (opt-in). - ScopeTest: ambient default, bump/reset cycle with zero steady-state allocations, overflow accounting, retain, model tracking/closing, and the weights-vs-activations separation. BCV dumps regenerated. Closes #1021 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
Local gate Targeted: JVM 55/55 and linuxX64 46/46 |
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.2 (milestone M1 #1002, PRD M1-F3 / M1-F4):
Scope— the lifetimes that separate weights from activations (§4.5). Both historical arena failures were violations of exactly this split: activations in a model-lifetime arena (tens of GB pinned) and per-call arenas (a leak per matmul).Scope(sealed):kind,liveBytes,sink,allocate(bytes, domain, origin),allocateFloats(count, origin).Scope.Ambient— the GC-managed default: untracked,close()is a no-op,val c = a matMul bin a notebook is unchanged (M1-F4).ModelScope— weights, KV backing, embedding tables: tracks everything it allocates, maps (mapFile) or adopts and closes it all exactly once, soModelScope().use { }unmaps the file and frees off-heap deterministically.ForwardScope(slabFloats)— activations: one pre-sized heap slab (the Phase-2 spike bench(spike): Phase-2 TensorView/Storage access-path spike — JMH benchmarks, flat-RSS loop, report (SKEEP-003 decision #6, S1.0) #1060 says heap activations by default on the JVM), bump-allocated as zero-copy slab views;reset()closes the views handed out, frees overflow and rewinds the offset, so steady-state decode allocates zero slab bytes (the test asserts exactly oneAllocationevent for the slab and none per step);usedFloats/peakFloats/overflowBytes/stepsfeed the planner and the plan-vs-actual check ([S1.9] M1: plan-vs-actual — allocation-event totals vsMemoryPlan, printed; CI assertion (> 10 % fails) #1030);retain(storage, to)is the one sanctioned escape (a copy) — everything else afterreset()is aStorageClosedException, not a use-after-free.ExecutionContext.memoryScopedefault member →Scope.Ambient(opt-in per context).ScopeTest: ambient default, bump/reset cycle with zero steady-state allocations, overflow accounting,retain, model tracking/closing, and the weights-vs-activations separation. BCV dumps regenerated (the newgetMemoryScope()propagates to lang-core, backend-cpu, compile-dag).Stacked on #1064 (#1020); retarget to
developas the chain merges. The JVMForwardslab overSegmentStorage(off-heap) and the debug escape check are follow-ups (#1026); this slice keeps the spike's rule: heap activations by default.Test plan
Full local gate (
scripts/pr-gate.sh, JDK 25) — all legs passed; results in the first comment. Targeted: JVM 55/55 and linuxX64 46/46sk.ainet.lang.memory.*tests.Closes #1021
🤖 Generated with Claude Code