Repository navigation
Deleting variables takes longer on Julia 1.13 #3124
Description
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 sWhere 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
odow commentedon Oct 29, 2026
This will teach me for using only v1.12.x locally...