For structures that contain among other things a mutable scalar, I see (at least) two approaches:
- Make the structure mutable
mutable struct S1
vector1::Vector{Float64}
vector2::Vector{Float64}
matrix::Matrix{Float64}
scalar::Float64
end
set_scalar!(a::S1, x) = a.scalar = x
get_scalar(a::S1) = a.scalar
- Use
Base.RefValue
struct S2
vector1::Vector{Float64}
vector2::Vector{Float64}
matrix::Matrix{Float64}
scalar::Base.RefValue{Float64}
end
set_scalar!(a::S2, x) = a.scalar[] = x
get_scalar(a::S2) = a.scalar[]
The first approach looks a bit nicer to read (even though I would have kept the structure immutable without the scalar). But based on the following silly benchmark, the second approach seems a tiny bit faster.
f(a) = get_scalar(a) * sum(a.vector1) + sum(a.vector2) + sum(a.matrix)
g!(a, s) = a.vector1 .*= s
function do_something(a)
b = prod(a.vector1) * prod(a.vector2)
b += get_scalar(a)
set_scalar!(a, .6)
g!(a, .4)
b *= get_scalar(a) + f(a)
g!(a, 1/.4)
set_scalar!(a, .4)
b
end
x1 = S1([1., 2.], [3., 4.], [1. 2.; 5. 0.], .4)
x2 = S2([1., 2.], [3., 4.], [1. 2.; 5. 0.], Ref(.4))
using BenchmarkTools
julia> @btime do_something($x1)
25.502 ns (0 allocations: 0 bytes)
398.20799999999997
julia> @btime do_something($x2)
22.462 ns (0 allocations: 0 bytes)
398.20799999999997
What is your favourite approach for such structures? Should I expect the performance penalty of the first approach (mutable struct
) to be very small most of the time?