Context
UnitMap wraps BTreeMap<String, i32> and is constructed from scratch every time units::Context::new is called. WRLD3 has ~311 unit definitions, each producing a UnitMap entry.
In the current incremental compilation architecture, unit contexts are rebuilt per-variable (see related work to cache the context at project level). Even after that fix, UnitMap cloning during unit inference (units_infer.rs) remains allocation-heavy: the ConstraintSet::substitute and unify functions clone UnitMap values hundreds of times during inference (138K + 128K bytes in the base branch DHAT profile).
Proposal
Wrap the inner BTreeMap in an Arc:
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnitMap(Arc<BTreeMap<String, i32>>);
This makes cloning O(1) (atomic refcount increment) instead of O(n) (deep copy of the BTreeMap). The UnitMap is immutable after construction, so shared ownership is safe.
Scope
- Change
UnitMap internals to use Arc<BTreeMap<String, i32>>
- Update
FromIterator impl to wrap in Arc
- Ensure
Hash, PartialEq, Eq, PartialOrd, Ord impls dereference through the Arc
- Verify unit inference (
units_infer.rs) benefits from cheap clones in ConstraintSet::substitute and unify
Acceptance Criteria
Notes
This is a targeted, low-risk optimization. UnitMap is never mutated after construction, making Arc a clean fit. The main risk is any code that currently pattern-matches or borrows the inner BTreeMap directly -- those sites need to dereference through the Arc.
Profiling data (WRLD3, base branch, single pipeline run):
- BTreeMap clone_subtree in UnitMap::clone: 138K + 128K bytes = ~266K total
- In unit inference unify/substitute loops: 833 allocations
Context
UnitMapwrapsBTreeMap<String, i32>and is constructed from scratch every timeunits::Context::newis called. WRLD3 has ~311 unit definitions, each producing aUnitMapentry.In the current incremental compilation architecture, unit contexts are rebuilt per-variable (see related work to cache the context at project level). Even after that fix,
UnitMapcloning during unit inference (units_infer.rs) remains allocation-heavy: theConstraintSet::substituteandunifyfunctions cloneUnitMapvalues hundreds of times during inference (138K + 128K bytes in the base branch DHAT profile).Proposal
Wrap the inner
BTreeMapin anArc:This makes cloning O(1) (atomic refcount increment) instead of O(n) (deep copy of the BTreeMap). The
UnitMapis immutable after construction, so shared ownership is safe.Scope
UnitMapinternals to useArc<BTreeMap<String, i32>>FromIteratorimpl to wrap in ArcHash,PartialEq,Eq,PartialOrd,Ordimpls dereference through the Arcunits_infer.rs) benefits from cheap clones inConstraintSet::substituteandunifyAcceptance Criteria
UnitMapclone is O(1) via Arccargo test -p simlin-enginepassescargo bench -p simlin-engine --bench compiler -- wrld3shows reduced allocation count inproject_buildunits_infer.rsNotes
This is a targeted, low-risk optimization. UnitMap is never mutated after construction, making Arc a clean fit. The main risk is any code that currently pattern-matches or borrows the inner BTreeMap directly -- those sites need to dereference through the Arc.
Profiling data (WRLD3, base branch, single pipeline run):