# Function compiles every time

I re-wrote my astrodynamics functions with `ModelingToolkit`. Now, when I call `DifferentialEquations.solve(...)` in my wrapper function, it compiles every time. Is there any way to check what is compiling?

``````using StaticArrays
using AstrodynamicalModels
using DifferentialEquations

function propagate(r, v, T; kwargs...)

defaults = (; reltol=1e-14, abstol=1e-14, save_everystep=false)
options  = merge(defaults, kwargs)

problem   = ODEProblem(R2BP, MVector{6}(vcat(r,v)), (0.0, T), MVector{1}(398600.435))
solutions = solve(problem; options...)

end

r = randn(3) * 1e4
v = randn(3) * 1e3
T = 10_000.0

@time propagate(r,v,T) #  28.067676 seconds (93.98 M allocations: 5.404 GiB, 3.61% gc time)
@time propagate(r,v,T) #   7.187065 seconds (13.05 M allocations: 704.718 MiB, 1.34% gc time, 99.70% compilation time)
@time propagate(r,v,T) #   7.154281 seconds (13.06 M allocations: 705.808 MiB, 1.79% gc time, 99.43% compilation time)
``````

If I don’t put the solver in a function, the behavior is fine.

``````using StaticArrays
using AstrodynamicalModels
using DifferentialEquations

defaults = (; reltol=1e-14, abstol=1e-14, save_everystep=false)
kwargs   = (;)
options  = merge(defaults, kwargs)

r = randn(3) * 1e4
v = randn(3) * 1e3
T = 10_000.0

problem   = ODEProblem(R2BP, MVector{6}(vcat(r,v)), (0.0, T), MVector{1}(398600.435))

@time solutions = solve(problem; options...) #  15.728376 seconds (50.96 M allocations: 2.763 GiB, 4.17% gc time, 2.34% compilation time)
@time solutions = solve(problem; options...) #   0.012221 seconds (540.66 k allocations: 9.171 MiB)
@time solutions = solve(problem; options...) #   0.012453 seconds (540.66 k allocations: 9.171 MiB)
``````

What am I doing wrong?

1 Like

Is `R2BP` an `ODESystem`? `ODESystem`s are symbolic objects, so every time you go symbolic → code it has to compile. I would recommend compiling it once and then using tools like `remake` to change `u0`, `tspan`, and `p`.

4 Likes

Ah I see. @ChrisRackauckas what constitutes “compiling” in this case? If I’m exporting an `ODESystem` that I don’t expect users to really change, should my package also export `ODEFunction` objects? Are `ODEFunction`s compiled?

I tried checking `Catalyst.jl` source code but couldn’t answer my question.

Yes they are.

It builds a new source for a function `f`, so it’ll need to compile again when it’s rebuilt. There may be a way to fix that though… @Chris_Foster is there a way to make runtimegeneratedfunctions cache this better? They should have the same hash.

1 Like

If the expressions have exactly the same hash this should already work.

Demo:

``````julia> using RuntimeGeneratedFunctions

julia> ex = :(x -> x^2 - 1)
:(x->begin
#= REPL:1 =#
x ^ 2 - 1
end)

julia> RuntimeGeneratedFunctions.init(Main)

julia> f = @RuntimeGeneratedFunction(ex)
RuntimeGeneratedFunction(#=in Main=#, #=using Main=#, :((x,)->begin
#= REPL:1 =#
x ^ 2 - 1
end))

julia> @time f(1.0)
0.051278 seconds (64.49 k allocations: 4.051 MiB, 99.96% compilation time)
0.0

julia> @time f(1.0)
0.000004 seconds (1 allocation: 16 bytes)
0.0

julia> g = @RuntimeGeneratedFunction(ex)
RuntimeGeneratedFunction(#=in Main=#, #=using Main=#, :((x,)->begin
#= REPL:1 =#
x ^ 2 - 1
end))

julia> @time g(1.0)
0.000008 seconds (1 allocation: 16 bytes)
0.0

julia> typeof(f) === typeof(g)
true
``````

Maybe the hashes aren’t exactly equal here for some reason?

1 Like
``````:(function (var"##out#1185", var"##arg#1183", var"##arg#1184", t)
#= C:\Users\accou\.julia\packages\SymbolicUtils\kZD5O\src\code.jl:282 =#
#= C:\Users\accou\.julia\packages\SymbolicUtils\kZD5O\src\code.jl:283 =#
let var"x(t)" = #= C:\Users\accou\.julia\packages\SymbolicUtils\kZD5O\src\code.jl:169 =# @inbounds(var"##arg#1183"), τ = #= C:\Users\accou\.julia\packages\SymbolicUtils\kZD5O\src\code.jl:169 =# @inbounds(var"##arg#1184")
#= C:\Users\accou\.julia\packages\Symbolics\sITWZ\src\build_function.jl:331 =#
#= C:\Users\accou\.julia\packages\SymbolicUtils\kZD5O\src\code.jl:329 =# @inbounds begin
#= C:\Users\accou\.julia\packages\SymbolicUtils\kZD5O\src\code.jl:325 =#
var"##out#1185" = (*)((inv)(τ), (+)(1, (*)(-1, var"x(t)")))
#= C:\Users\accou\.julia\packages\SymbolicUtils\kZD5O\src\code.jl:327 =#
nothing
end
end
end)
(generate_function(sys)) = :(function (var"##out#1188", var"##arg#1186", var"##arg#1187", t)
#= C:\Users\accou\.julia\packages\SymbolicUtils\kZD5O\src\code.jl:282 =#
#= C:\Users\accou\.julia\packages\SymbolicUtils\kZD5O\src\code.jl:283 =#
let var"x(t)" = #= C:\Users\accou\.julia\packages\SymbolicUtils\kZD5O\src\code.jl:169 =# @inbounds(var"##arg#1186"), τ = #= C:\Users\accou\.julia\packages\SymbolicUtils\kZD5O\src\code.jl:169 =# @inbounds(var"##arg#1187")
#= C:\Users\accou\.julia\packages\Symbolics\sITWZ\src\build_function.jl:331 =#
#= C:\Users\accou\.julia\packages\SymbolicUtils\kZD5O\src\code.jl:329 =# @inbounds begin
#= C:\Users\accou\.julia\packages\SymbolicUtils\kZD5O\src\code.jl:325 =#
var"##out#1188" = (*)((inv)(τ), (+)(1, (*)(-1, var"x(t)")))
#= C:\Users\accou\.julia\packages\SymbolicUtils\kZD5O\src\code.jl:327 =#
nothing
end
end
end)
``````

Every time it generates the function it increments the gensyms. There’s probably a way to fix that? It’s all fine locally since these functions can’t hit globals.

3 Likes

Nice! Yes I wondered if it was upstream a little from `RuntimeGeneratedFunctions`.