Context
After the 2026-05-31 LTM compile-time optimization work (commit bfff92e8 "engine: fix O(N^2) blowups in LTM compilation", through the bfff92e8..51392894 cluster on main), LTM compilation of the C-LEARN v77 model dropped from ~47.6s to ~6s. Profiling the residual ~6s shows the remaining cost is dominated by per-fragment fixed overhead that is paid once per LTM synthetic variable + helper. C-LEARN mints ~37k of these, so even small per-fragment constants dominate.
The two hot fragment-compile entry points are in src/simlin-engine/src/db/ltm/compile.rs:
compile_ltm_equation_fragment (around line 241; Module built at lines 934-935)
compile_ltm_implicit_var_fragment (around line 1187; Module built at lines 1732-1733)
Findings
1. Per-fragment deep-clone of the DimensionsContext and converted dims
Each per-fragment compiler::Module construction does:
dimensions_ctx: (*dim_context).clone(),
dimensions: converted_dims.to_vec(),
(compile.rs:934-935 and compile.rs:1732-1733)
DimensionsContext is a HashMap of dimensions with per-dimension element maps; cloning it (plus the converted-dims Vec) per fragment deep-copies the entire project dimension set ~37k times for C-LEARN.
Fix direction: make DimensionsContext / Dimension internals Arc-backed so a clone is a refcount bump, or let Module borrow them rather than own a clone.
2. Compiler::new -> populate_dimension_metadata interns names with an O(D^2) linear scan, then discards the result
Compiler::new (src/simlin-engine/src/compiler/codegen.rs:49) calls populate_dimension_metadata (codegen.rs:87), which iterates all project dimensions per fragment and interns each dimension/element name via intern_name (codegen.rs:109). intern_name is a linear scan over the names vec:
if let Some(idx) = self.names.iter().position(|n| n == name) {
return idx as NameId;
}
That is O(D^2) string comparisons per fragment compile, repeated ~37k times. The dimension tables produced this way are discarded for fragment compiles -- the assembly stage builds its own.
Fix direction: hash-based interning for intern_name, or skip dimension-metadata population entirely for per-variable fragment compilation (the produced tables are unused on this path).
3. Each LTM equation is parsed 2-3 times during assembly
The same LTM equation text is parsed repeatedly across:
model_ltm_implicit_var_info
compile_ltm_synthetic_fragment
assemble_module's implicit-var loop (via parse_ltm_var_with_ids)
Fix direction: parse once and thread the parsed AST through, or memoize the parse keyed on the synthetic var.
4. Identifier canonicalization/interning under a global lock is ~15-20% of compile time
CanonicalStorage::intern (src/simlin-engine/src/common.rs:226) routes through the global sharded interner (Interner::global(), common.rs:72 -- an array of Mutex<Shard>). At ~37k fragments this lock traffic is ~15-20% of compile time.
This overlaps with #317 (intern Ident<Canonical> via salsa) -- #317 would reduce the general hashing/cloning cost, but the LTM fragment path's volume of intern calls is the specific amplifier here, and findings 1-3 above are LTM-fragment-specific and not covered by #317.
Why it matters
Maintainability and developer/agent experience: 6s to compile a single (large, but real) model with LTM enabled is slow enough to discourage iterative use of LTM on production-scale models. The remaining cost is fixed overhead multiplied by synthetic-var count, so it scales poorly as models grow. None of it is intrinsic work -- findings 1-3 are pure waste (deep-cloning immutable data, computing-then-discarding dimension tables, re-parsing identical text).
Components affected
Relationship to existing issues
Discovered
Identified during profiling of the 2026-05-31 LTM compile-time optimization work; deferred as the next layer of compile-time reduction after the O(N^2) blowups were removed.
Context
After the 2026-05-31 LTM compile-time optimization work (commit
bfff92e8"engine: fix O(N^2) blowups in LTM compilation", through thebfff92e8..51392894cluster onmain), LTM compilation of the C-LEARN v77 model dropped from ~47.6s to ~6s. Profiling the residual ~6s shows the remaining cost is dominated by per-fragment fixed overhead that is paid once per LTM synthetic variable + helper. C-LEARN mints ~37k of these, so even small per-fragment constants dominate.The two hot fragment-compile entry points are in
src/simlin-engine/src/db/ltm/compile.rs:compile_ltm_equation_fragment(around line 241;Modulebuilt at lines 934-935)compile_ltm_implicit_var_fragment(around line 1187;Modulebuilt at lines 1732-1733)Findings
1. Per-fragment deep-clone of the DimensionsContext and converted dims
Each per-fragment
compiler::Moduleconstruction does:(
compile.rs:934-935andcompile.rs:1732-1733)DimensionsContextis aHashMapof dimensions with per-dimension element maps; cloning it (plus the converted-dimsVec) per fragment deep-copies the entire project dimension set ~37k times for C-LEARN.Fix direction: make
DimensionsContext/DimensioninternalsArc-backed so a clone is a refcount bump, or letModuleborrow them rather than own a clone.2.
Compiler::new->populate_dimension_metadatainterns names with an O(D^2) linear scan, then discards the resultCompiler::new(src/simlin-engine/src/compiler/codegen.rs:49) callspopulate_dimension_metadata(codegen.rs:87), which iterates all project dimensions per fragment and interns each dimension/element name viaintern_name(codegen.rs:109).intern_nameis a linear scan over the names vec:That is O(D^2) string comparisons per fragment compile, repeated ~37k times. The dimension tables produced this way are discarded for fragment compiles -- the assembly stage builds its own.
Fix direction: hash-based interning for
intern_name, or skip dimension-metadata population entirely for per-variable fragment compilation (the produced tables are unused on this path).3. Each LTM equation is parsed 2-3 times during assembly
The same LTM equation text is parsed repeatedly across:
model_ltm_implicit_var_infocompile_ltm_synthetic_fragmentassemble_module's implicit-var loop (viaparse_ltm_var_with_ids)Fix direction: parse once and thread the parsed AST through, or memoize the parse keyed on the synthetic var.
4. Identifier canonicalization/interning under a global lock is ~15-20% of compile time
CanonicalStorage::intern(src/simlin-engine/src/common.rs:226) routes through the global sharded interner (Interner::global(),common.rs:72-- an array ofMutex<Shard>). At ~37k fragments this lock traffic is ~15-20% of compile time.This overlaps with #317 (intern
Ident<Canonical>via salsa) -- #317 would reduce the general hashing/cloning cost, but the LTM fragment path's volume of intern calls is the specific amplifier here, and findings 1-3 above are LTM-fragment-specific and not covered by #317.Why it matters
Maintainability and developer/agent experience: 6s to compile a single (large, but real) model with LTM enabled is slow enough to discourage iterative use of LTM on production-scale models. The remaining cost is fixed overhead multiplied by synthetic-var count, so it scales poorly as models grow. None of it is intrinsic work -- findings 1-3 are pure waste (deep-cloning immutable data, computing-then-discarding dimension tables, re-parsing identical text).
Components affected
src/simlin-engine/src/db/ltm/compile.rs(fragment-compile entry points)src/simlin-engine/src/compiler/codegen.rs(Compiler::new,populate_dimension_metadata,intern_name)src/simlin-engine/src/common.rs(global interner; shared with Intern Ident<Canonical> strings via salsa::interned to reduce hashing and cloning overhead #317)DimensionsContext/Dimensiondefinitions (for the Arc-backed-clone direction)Relationship to existing issues
bfff92e8and thebfff92e8..51392894cluster).Ident<Canonical>interning) for finding 4 only; findings 1-3 are LTM-fragment-specific and independent.Discovered
Identified during profiling of the 2026-05-31 LTM compile-time optimization work; deferred as the next layer of compile-time reduction after the O(N^2) blowups were removed.