Why does add-assign to Ref{Float64} allocate?

While optimizing some ODE code, I noticed that += on Ref{Float64} allocates, while .+= on a vector slice doen’t. Why is that?

I wasn’t able to reproduce the effect without DifferentialEquations, but I feel like it should be possible.
In practice I’m using Params for preallocation. Is that the correct design decision?

using Revise
using DifferentialEquations

struct Params
    x::Ref{Float64}
end

Params() = Params(0.0)

function f!(du::Vector{Float64}, u::Vector{Float64}, p::Params, t::Float64)
    p.x[] += 1.0 # 3168 bytes allocated
    du[1] = -u[1]
end

function run()
    u0 = [1.0]
    tspan = (0.0, 1.0)
    p = Params(0.0)
    ode_problem = ODEProblem(f!, u0, tspan, p)
    _ = solve(ode_problem, Tsit5(), reltol=1e-8, abstol=1e-8)
    println(p)
end

run()

Ref is an abstract type. Use RefValue in your struct definition instead.

3 Likes

Thanks, worked. Is there canonical documentation on abstract types as struct fields?

See the Performance tips: Avoid fields with abstract type.

3 Likes