Repository navigation
Lever C: stream/evict resolved-module envs (representation-minimization plan item 2) — design-first - #5886
Lever C: stream/evict resolved-module envs (representation-minimization plan item 2) — design-first#5886briansrls wants to merge 7 commits into
Conversation
Project each TypedModule into a fresh one keeping module/items/item_registry but pointing type_env/func_env at shared empty singletons, so the floor path releases the never-read envs once the source ResolvedGraph drops. Remove probe scaffold; untrack scratchpad. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ept, only envs evicted) The teeth perturbation (items emptied) was auto-committed/pushed; it served its purpose as the discriminating control (rust_tests RED with items evicted vs GREEN with only type_env/func_env evicted), proving the reflection path consumes items/item_registry. Restoring the intended change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Dropping per design review (manager stern-moth-225): this is bulk env-eviction at InterpContext construction = SUSTAINED-RSS only; it does not move the per-shard resolve/infer PEAK, which is the actual floor OOM axis (#5877 cgroup-bound concurrency). The verified §2 finding (interpreter never reads type_env/func_env; teeth-proven) and the root design (frontier-bounded retention over the existing v2 ResolvedTree/InferredTree split) are captured in the representation-minimization plan. Whether to implement peak-reducing frontier-evict in the doomed v1 seed (A1) vs record it as a design note until v2 infer is the live path (A3) is being decided by a per-shard-peak-vs-cap measurement after the cheaper fixes (#5884 + alloc-clamp). — sent from zesty-crab-811 |
Drop the abandoned v1_interpreter.rs bulk-eviction (PR #5886 closed: sustained-RSS only, did not move the per-shard resolve peak). Record the Lever C design outcome in the plan .dag authority + regenerate the .md projection (main_wet): - §2 finding (floor consumes the resolved Node tree, not type_env/func_env; teeth-proven) and extend-not-fork (aligns to v2 ResolvedTree/InferredTree, no new type); - the v2-surviving root is a frontier-bounded RETENTION POLICY over InferredTree.facts (OWED at v2 00_compile, which holds it whole today); - A3-default conditional with an explicit flip-to-A1 trigger (implement v1 frontier-evict only if a residual per-shard peak survives over cap after the cheaper fixes — alloc-clamp #5877 landed + Rc-shared sigs #5884 pending). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…und; A3-default + flip trigger) (#5892) * WIP: Lever C: stream/evict resolved-module envs (representation-minimization * WIP: Lever C: stream/evict resolved-module envs (representation-minimization * Lever C: evict resolved-module envs the interpreter never reads Project each TypedModule into a fresh one keeping module/items/item_registry but pointing type_env/func_env at shared empty singletons, so the floor path releases the never-read envs once the source ResolvedGraph drops. Remove probe scaffold; untrack scratchpad. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * WIP: Lever C: stream/evict resolved-module envs (representation-minimization * WIP: Lever C: stream/evict resolved-module envs (representation-minimization * Restore clean #5886 (revert teeth perturbation; items/item_registry kept, only envs evicted) The teeth perturbation (items emptied) was auto-committed/pushed; it served its purpose as the discriminating control (rust_tests RED with items evicted vs GREEN with only type_env/func_env evicted), proving the reflection path consumes items/item_registry. Restoring the intended change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * WIP: Lever C: stream/evict resolved-module envs (representation-minimization * Lever C (item 2): record design-note disposition in the plan authority Drop the abandoned v1_interpreter.rs bulk-eviction (PR #5886 closed: sustained-RSS only, did not move the per-shard resolve peak). Record the Lever C design outcome in the plan .dag authority + regenerate the .md projection (main_wet): - §2 finding (floor consumes the resolved Node tree, not type_env/func_env; teeth-proven) and extend-not-fork (aligns to v2 ResolvedTree/InferredTree, no new type); - the v2-surviving root is a frontier-bounded RETENTION POLICY over InferredTree.facts (OWED at v2 00_compile, which holds it whole today); - A3-default conditional with an explicit flip-to-A1 trigger (implement v1 frontier-evict only if a residual per-shard peak survives over cap after the cheaper fixes — alloc-clamp #5877 landed + Rc-shared sigs #5884 pending). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Lever C: record RESOLVED A3 verdict in the plan note (measured, not data-gated) Official call (manager stern-moth-225): A3, robust. Post-#5873-cutover floor measurement (claim_executor per-shard VmHWM × spawn_width=2, sccache excluded): co-resident 5.98 GiB, 1.82 GiB UNDER the ~7.8 GiB safety threshold below the 8 GiB host cap, +0.7% vs the 5.94 GiB pre-cutover baseline (noise). The cheaper fixes already hold the floor under cap; frontier-evict is NOT written into the doomed v1 seed — the ~4 GiB prize is captured durably in v2 streaming-infer. Flip-to-A1 trigger kept documented but marked UN-tripped (re-auditable if the corpus grows). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Lever C — evict resolved-module envs the interpreter never reads
Representation-minimization plan (
docs/plans/representation-minimization.md,#5880) item 2. Attacks the sustained floor RSS: the resolved graph is held
resident for the whole long claim-execution phase, and most of what it holds is dead
weight to the interpreter.
Finding (by code read, then by execution)
Whole-tree resolve produces
ResolvedGraph { modules: Vec<TypedModule> }. EachTypedModulecarries a heavytype_env: Rc<TypeEnv>(per-symbolbindingsofresolved type Nodes + provenance) and
func_env: Rc<ResolvedFuncEnv>(signaturemap).
InterpContext::with_runtime_optionsthen buildsfn_nodes/service_ops/the global
item_registryup front and storesmodules: graph.modules.clone().After construction the interpreter never reads
type_envorfunc_env— theonly eval-time reads of the stored modules (
v1_interpreter.rs:2686,coproduct_reflection.rs:69/466/767,wire_value_serialize.rs:130) touch onlymodule/items/ per-moduleitem_registry. Zero.type_env/.func_envreads exist in the interpreter or its external module readers. So those payloads are
a representation the consumer never consults (DESIGN §2).
Change
evict_module_envs(new) projects eachTypedModuleinto a fresh one that keepsmodule/items/item_registry(same Rcs — cheap refcount bumps) but pointstype_env/func_envat shared empty singletons (empty_intern_table()alreadyexists).
with_runtime_optionsbuilds the storedmodulesthrough it. When thecaller drops its
Rc<ResolvedGraph>(the floor pathwhole_tree_resolved_ctxdropsgraphat return), the real env payloads become reclaimable.the envs) just pay ~600 small struct allocs (no win, no harm); the win materializes
on the floor path where the graph is dropped.
Magnitude (executed, live 462-module
dslcorpus)A one-off probe serializing the resolved modules before vs after eviction:
93.2% of the serialized resolved-module representation is the evicted
type_env/func_env. Caveat on reading this number: serialized size overstatesthe in-memory footprint because
serde_jsonexpands Rc-shared structure (the sharedInternTable from #5867, and any resolved Nodes shared with
items, are counted onceper reference). It is therefore a relative representation-size metric, not an RSS
measurement — the in-memory reclamation is a fraction of 5.33 GB. RSS itself is not a
clean A/B target here: eviction does not lower the resolve peak (all envs are still
built before projection), and glibc does not return freed heap to the OS on a
short run; the benefit is steady-state heap reuse during the long claim phase.
Scope boundary (honest)
This is the evict half of Lever C — it removes the never-read envs from the
sustained interpreter working set. It does not cut the transient typecheck peak
(the deeper "resolve in dependency batches, evict each env once its dependents are
typechecked" restructures the load-bearing typecheck pipeline). Since v1 is going
away, that deeper batch/stream is documented as the v2-root follow-on rather than
built into the doomed seed. The emit path (needs all envs) is untouched and is not
the floor-OOM driver.
Design note:
scratchpad/lever-c-design.md(worktree-local, not committed).