Skip to content

Deleting variables takes longer on Julia 1.13 #3124

Closed

Description

@trulsf

During testing I encountered a regression on the time to delete variables from MOI models. The following is a write up of the regression from Claude:

Deleting a variable from an MOI.Utilities.Model{Float64} takes 50–120 s the first
time in a session on Julia 1.13. All of that time is compilation. Julia 1.12 and earlier
take 2–6 s. The MathOptInterface version is the same in all cases (1.54.0).

Every JuMP model is affected, since JuMP.delete(model, x) reaches this method
through the model cache.

Reproduction

import MathOptInterface as MOI

model = MOI.Utilities.Model{Float64}()
x = MOI.add_variable(model)
@time MOI.delete(model, x)

Timings

Each timing is from a fresh process.

Windows (x86_64):

Julia @time MOI.delete(model, x)
1.10.12 2.0–3.1 s
1.11.9 3.7 s
1.12.7 3.7–5.6 s
1.13.3 53–121 s (100% compilation)

Linux (x86_64):

Julia @time MOI.delete(model, x) Allocations
1.10.12 1.5 s 372 k / 25 MiB
1.11.9 1.9 s 745 k / 38 MiB
1.12.7 2.3 s 854 k / 42 MiB
1.13.1 97 s (100% compilation) 45.3 M / 2.27 GiB
1.13.3 93–97 s (100% compilation) 45.8 M / 2.29 GiB

The regression is not specific to Windows. On Linux it is already present in 1.13.1, and
allocations during compilation grow about 50× from 1.12 to 1.13.

The JuMP version shows the same pattern (JuMP 1.32.1):

using JuMP
m = Model(); @variable(m, x)
@time delete(m, x)   # 1.10: 1.6 s, 1.13: 53 s

Where the time goes

--trace-compile-timing on 1.13 attributes all of the time to a single method instance:

#= 109895.6 ms =# precompile(Tuple{typeof(MathOptInterface.delete),
    MathOptInterface.Utilities.GenericModel{Float64,
        MathOptInterface.Utilities.ObjectiveContainer{Float64},
        MathOptInterface.Utilities.VariablesContainer{Float64},
        MathOptInterface.Utilities.ModelFunctionConstraints{Float64}},
    MathOptInterface.VariableIndex})

Each step of MOI.delete(::AbstractModel, ::VariableIndex) was timed on its own, in a
fresh process:

Call 1.10 1.13
Utilities._deleted_constraints(cb, model.constraints, x) 0.73 s 100.2 s
Utilities._throw_if_cannot_delete(model.constraints, [x], x) 0.91 s 6.0 s
MOI.delete(model.objective, x) 1.64 s 0.74 s
MOI.delete(model.variables, x) 0.02 s 0.02 s

Both slow calls go through broadcastcall over the StructOfConstraints. That applies
a closure to every function/set field of ModelFunctionConstraints. This suggests that
inference or compilation of broadcastcall over the large generated struct regressed
in Julia 1.13. A change in MOI is less likely, since 1.12 and earlier are unaffected
with the same MOI version.

Environment

Windows:

Julia Version 1.13.3
Commit 67ac47b9cd (2026-10-25 15:28 UTC)
OS: Windows (x86_64-w64-mingw32)
CPU: 8 × Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz
LLVM: libLLVM-20.1.8 (ORCJIT, skylake)
Threads: 1 default, 1 interactive, 1 GC
MathOptInterface v1.54.0

Julia 1.10.12, 1.11.9 and 1.12.7 are official releases installed with juliaup on the
same machine.

Linux:

Julia Version 1.13.3
Commit 67ac47b9cd8 (2026-10-25 15:28 UTC)
OS: Linux (x86_64-linux-gnu)
CPU: 8 × Intel Xeon E312xx (Sandy Bridge, IBRS update), KVM guest
LLVM: libLLVM-20.1.8 (ORCJIT, sandybridge)
Threads: 1 default, 1 interactive, 1 GC
MathOptInterface v1.54.0

All Julia versions are official releases installed with juliaup. On 1.10 and 1.11 the
environment was re-resolved because Manifest.toml was written by 1.12. MathOptInterface
stays at 1.54.0 through the [compat] entry.

Activity

  1. odow commented on Oct 29, 2026

    @odow
    Member

    This will teach me for using only v1.12.x locally...

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions