Sienna-Platform / Sienna-Platform/InfrastructureOptimizationModels.jl

OptimizationContainer's type-erased storage forces per-timestep dynamic dispatch in consumer build loops

Open
#155 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Julia
Stars
1
Forks
0
Avg merge
8h 51m
Merged PRs (30d)
9

Description

Summary

OptimizationContainer's field containers are type-erased, so every per-timestep read of a variable, parameter, or expression in a consumer's build loop is an ::Any access resolved by dynamic dispatch. This is the dominant type-instability in the optimization stack — every other hot-path finding downstream compounds through it.

Details

src/core/optimization_container.jl:74-80:

  • variables / aux_variables / duals / constraints / expressions are OrderedDict{<KeyType>, JuMPArray} where JuMPArray = Union{JuMP.Containers.DenseAxisArray, JuMP.Containers.SparseAxisArray} (src/core/definitions.jl:80) — the union carries no element/axis parameters, so retrieval erases everything.
  • parameters::OrderedDict{ParameterKey, ParameterContainer} (:80) stores ParameterContainer{T,U,A} values with the parameters stripped.
  • default_time_series_type::Type (:95) is an abstract Type field; every consumer keying off it (get_default_time_series_type call sites in parameter/expression construction) pays a method lookup per call.

Consequence: get_variable / get_parameter / get_expression return values whose type parameters are gone, so indexing like multiplier[name, t] or variable[name, t] inside for t in time_steps loops returns ::Any and every downstream operation redispatches dynamically per (device, timestep).

Measured with a faithful minimal reproduction of the ParameterContainer{T,U,A}-in-erased-dict shape (Julia 1.12.5): Base.return_types gives Any on the read path, and a 200-iteration read loop allocates 6480 bytes vs 0 bytes for the concretely-typed equivalent.

Secondary, same mechanism at lower multiplicity:

  • Dict{String, AbstractArray} staging in consumers' _add_time_series_parameters!-style code erases the concrete Vector{Float64} the accessors actually return, forcing per-step redispatch into _set_parameter_at! (which itself has clean concrete dispatch arms — the callers hand it Any).

Suggested fix direction

This is architecture-level, not a local patch:

  1. A typed per-(EntryType, ComponentType) cache — the dict values become concretely parameterized per key type, function-barrier style, so the container hand-off into the timestep loop specializes; or
  2. A return-type-asserting barrier at the get_variable/get_parameter/get_expression boundary (callers state the expected concrete container type), keeping the storage erased but the loop bodies monomorphic; and
  3. Parameterize the container (or thread a Type{T}/Val{T} argument) for default_time_series_type instead of the abstract Type field.

Found during a type-stability audit of the time-series chain (details reproducible with @code_warntype on any consumer build-loop read).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/core/optimization_container.jl:74-80 and :95, then inspect the JuMPArray definition in src/core/definitions.jl:80 and the get_variable, get_parameter, and get_expression call sites in consumer build loops. Use @code_warntype and the stated minimal reproduction to trace the erased types and allocations. Done means the selected architecture removes the per-timestep Any dispatch while preserving the container and consumer behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
julia
Domain
backend, performance
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.