Skip to content

feat(memory): Scope — Model / Forward / Ambient lifetimes; recycled Forward slab with reset() and retain() (SKEEP-003 P2, S1.2) - #1065

Merged
michalharakal merged 1 commit into
developfrom
feature/1021-scopes
Aug 23, 2026
Merged

michalharakal merged 1 commit into
developfrom
feature/1021-scopes

Conversation

@michalharakal

Copy link
Copy Markdown
Contributor

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 b in 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, so ModelScope().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 one Allocation event for the slab and none per step); usedFloats / peakFloats / overflowBytes / steps feed the planner and the plan-vs-actual check ([S1.9] M1: plan-vs-actual — allocation-event totals vs MemoryPlan, printed; CI assertion (> 10 % fails) #1030); retain(storage, to) is the one sanctioned escape (a copy) — everything else after reset() is a StorageClosedException, not a use-after-free.
  • ExecutionContext.memoryScope default 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 new getMemoryScope() propagates to lang-core, backend-cpu, compile-dag).

Stacked on #1064 (#1020); retarget to develop as the chain merges. The JVM Forward slab over SegmentStorage (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/46 sk.ainet.lang.memory.* tests.

Closes #1021

🤖 Generated with Claude Code

…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>
@michalharakal

Copy link
Copy Markdown
Contributor Author

Local gate scripts/pr-gate.sh (JDK 25) on aaaff96: all legs passed — jvmTest · apiCheck · JS/Wasm · linuxX64Test · assemble · Java API tests. (First run failed on apiCheck only: the new ExecutionContext.memoryScope getter propagates into the lang-core, backend-cpu and compile-dag dumps; regenerated with apiDump.)

Targeted: JVM 55/55 and linuxX64 46/46 sk.ainet.lang.memory.* tests, including the zero-steady-state-allocation assertion on the Forward slab.

Base automatically changed from feature/1020-storage-native-wasm to develop August 23, 2026 12:46
@michalharakal
michalharakal merged commit c3b3098 into develop Aug 23, 2026
4 checks passed
@michalharakal
michalharakal deleted the feature/1021-scopes branch August 23, 2026 12:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[S1.2] P2: Scope Model/Forward/Ambient — JVM Arena bump allocator, reset(), retain(), escape check; ExecutionContext opt-in

1 participant