Time to build system and problem is slower in Julia 1.12 with MTKv11

Hi, while I was trying the new version (v11) of ModelingToolkit.jl , I noticed it took longer to build the ODE system and problem under Julia 1.12.

Here is the GitHub actions workflow running the script

using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D
using OrdinaryDiffEq

@parameters τ = 3.0 # parameters
@variables x(t) = 0.0 # dependent variables
eqs = [D(x) ~ (1 - x) / τ]

@time "Build system" @mtkcompile fol = System(eqs, t)
@time "Build problem" prob = ODEProblem(fol, [], (0.0, 10.0))
@time "Solve problem" sol = solve(prob, Tsit5())

Time for building the ODESystem / ODEProblem in seconds.

MTK JL1.10 JL1.12
v10 1.25/2.04 0.76/1.89
v11 1.91/1.35 33.44/17.37

Is there anything I can do to reduce the first time to build systems/problems in 1.12?

Hi! Thanks for reporting this. This is certainly very abnormal, and I’ve gone to great lengths to ensure that TTFX of MTKv11 on 1.12 is as fast as possible. I’ll investigate what’s going on here. Before I do, I’ll need a little more information.

  • What versions of ModelingToolkit, ModelingToolkitBase, ModelingToolkitTearing, Symbolics and SymbolicUtils are you running?
  • Did you run the above code in the REPL or as a script?
  • What packages do you have in your global environment on each Julia version?
  • Did you use the --project flag when starting Julia? If you didn’t, and if you ran the code in a script file, how did you load the appropriate environment?

All questions should be answered in the linked CI workflow? fix · sosiristseng/mtk-ttfx@68045eb · GitHub

Ah, thanks. Somehow I glossed over that part.

A quick mockup of your example indeed reproduces compile time differences, but not nearly as significant as in your CI. I get

Julia 1.10:

Build system: 0.662157 seconds (1.43 M allocations: 106.126 MiB, 1.52% gc time, 99.15% compilation time)
Build problem: 0.568265 seconds (1.26 M allocations: 90.375 MiB, 1.43% gc time, 94.27% compilation time)

Julia 1.12:

Build system: 2.221802 seconds (2.72 M allocations: 139.684 MiB, 1.89% gc time, 99.62% compilation time: 100% of which was recompilation)
Build problem: 3.862440 seconds (25.89 M allocations: 1.300 GiB, 2.59% gc time, 99.53% compilation time: 62% of which was recompilation)

All of the time is Julia compile time. I’m not yet sure why it increased so dramatically.

Thanks for the reply.

  1. I need to print manifest in the CI.
  2. I ran it as a script: julia test.jl
  3. There is no global packages installed (prior to point 4). The CI spins a fresh environment every time.
  4. By point 3, I just install all packages in the global environment.

Edit: Added pkg versions

  [961ee093] ModelingToolkit v11.9.0
  [7771a370] ModelingToolkitBase v1.12.0
  [6bb917b9] ModelingToolkitTearing v1.2.6
  [d1185830] SymbolicUtils v4.17.0
  [0c5d862f] Symbolics v7.13.0

Output of versioninfo():

Julia Version 1.10.10
Commit 95f30e51f41 (2025-06-27 09:51 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 4 × AMD EPYC 7763 64-Core Processor
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-15.0.7 (ORCJIT, znver3)
Threads: 4 default, 0 interactive, 2 GC (on 4 virtual cores)
Environment:
  JULIA_CI = true
  JULIA_NUM_THREADS = auto
Julia Version 1.12.4
Commit 01a2eadb047 (2026-01-06 16:56 UTC)
Build Info:
  Official https://julialang.org release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 4 × AMD EPYC 7763 64-Core Processor
  WORD_SIZE: 64
  LLVM: libLLVM-18.1.7 (ORCJIT, znver3)
  GC: Built with stock GC
Threads: 4 default, 1 interactive, 4 GC (on 4 virtual cores)
Environment:
  JULIA_CI = true
  JULIA_NUM_THREADS = auto

It seems something invalidated the mtkcompile method recently, so it doesn’t precompile.

It seems like TTFX in Julia 1.12 is much faster after MTK v11.13, demonstrated by this GitHub CI run.

Thank you SciML team!

Here’s an update. I wonder if package loading affects invalidations.

I found that adding a line using Plots might increase time to build first modeling significantly.

using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D
using OrdinaryDiffEq

## HH neuron model for example
function hh_sys(; name=:hh)
    exprel(x) = x / expm1(x)
    @discretes iStim(t)=0.0
    @parameters E_N=55 E_K=-72 E_LEAK=-49 G_N_BAR=120 G_K_BAR=36 G_LEAK=0.3 C_M=1
    @variables v(t)=-59.8977 m(t)=0.0536 h(t)=0.5925 n(t)=0.3192 iNa(t) iK(t) iLeak(t) ma(t) mb(t) ha(t) hb(t) na(t) nb(t)

    ## Electrical stimulation events
    stim_on_1 = ModelingToolkit.SymbolicDiscreteCallback([20.0] => [iStim ~ -6.6], discrete_parameters = iStim, iv = t)
    stim_off_1 = ModelingToolkit.SymbolicDiscreteCallback([21.0] => [iStim ~ 0.0], discrete_parameters = iStim, iv = t)
    stim_on_2 = ModelingToolkit.SymbolicDiscreteCallback([60.0] => [iStim ~ -6.9], discrete_parameters = iStim, iv = t)
    stim_off_2 = ModelingToolkit.SymbolicDiscreteCallback([61.0] => [iStim ~ 0.0], discrete_parameters = iStim, iv = t)

    eqs = [
        ma ~ exprel(-0.10 * (v + 35)),
        mb ~ 4.0 * exp(-(v + 60) / 18.0),
        ha ~ 0.07 * exp(-(v + 60) / 20),
        hb ~ 1 / (exp(-(v + 30) / 10) + 1),
        na ~ 0.1 * exprel(-0.1 * (v + 50)),
        nb ~ 0.125 * exp(-(v + 60) / 80),
        iNa ~ G_N_BAR * (v - E_N) * (m^3) * h,
        iK ~ G_K_BAR * (v - E_K) * (n^4),
        iLeak ~ G_LEAK * (v - E_LEAK),
        D(v) ~ -(iNa + iK + iLeak + iStim) / C_M,
        D(m) ~ -(ma + mb) * m + ma,
        D(h) ~ -(ha + hb) * h + ha,
        D(n) ~ -(na + nb) * n + na
    ]
    sys = System(eqs, t; name, discrete_events=[stim_on_1, stim_off_1, stim_on_2, stim_off_2])
    return sys
end

## Fast, took less than 10 seconds
tend = 100.0
@time "Build system" @mtkcompile sys = hh_sys()
@time "Build problem" prob = ODEProblem(sys, [], tend)
@time "Solve problem" sol = solve(prob, TRBDF2())

But if I also load Plots.jl alongside MTK, it took much longer to build packages.

using ModelingToolkit
using ModelingToolkit: t_nounits as t, D_nounits as D
using OrdinaryDiffEq
using Plots ## <------ This line
Plots.default(size=(800, 600))
#====
The same HH neuron model above
====#

## Slow, took a minute on my computer
tend = 100.0
@time "Build system" @mtkcompile sys = hh_sys()
@time "Build problem" prob = ODEProblem(sys, [], tend)
@time "Solve problem" sol = solve(prob, TRBDF2())

I am reproducing this on GitHub actions: add info · sosiristseng/mtk-ttfx@984825a · GitHub

I would like to work around (or even better, resolve) this issue. What could I do?

We will retime after the OrdinaryDiffEq.jl v7 which greatly reduces invalidations

Thank you for your prompt reply, @ChrisRackauckas. Currently I could work around it through a startup package with PrecompileTools.@recompile_invalidations: Home · PrecompileTools.jl.

Yes, precompilation after invalidations, i.e. in your environment, does solve it.

In case someone want to know how to do this: mtk-ttfx/Startup/src/Startup.jl at main · sosiristseng/mtk-ttfx · GitHub

I also tried @recompile_invalidations but sometimes it generated a warning AssertionError(msg="irinterp is unable to handle heavy recursion correctly").