Skip to content

Make UnitMap cheaply cloneable via Arc to reduce per-context allocation overhead #318

Description

@bpowers

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

  • UnitMap clone is O(1) via Arc
  • cargo test -p simlin-engine passes
  • cargo bench -p simlin-engine --bench compiler -- wrld3 shows reduced allocation count in project_build
  • Unit inference tests pass with no behavioral changes
  • DHAT shows reduced BTreeMap clone allocations in units_infer.rs

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

Activity

  1. added
    engineIssues with the rust-based simulation engine
    on Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    engineIssues with the rust-based simulation engine

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions