A variant of the @generated function approach could be to generate “somehow” a “set_field!” function explicitly with a macro once for every type involved and then call these functions. Below is a test (replacing the macro by manually providing the set_fields! functions for simplicity):
module Test3
mutable struct Vars1
v1::Float64
v2::Float64
v3::Float64
v4::Float64
v5::Float64
end
function set_fields!(v::Vars1, x::Vector{Float64}, j::Int)
v.v1 = x[j]
v.v2 = x[j+1]
v.v3 = x[j+2]
v.v4 = x[j+3]
v.v5 = x[j+4]
end
mutable struct Vars2
r1::Float64
r2::Float64
r3::Float64
r4::Float64
r5::Float64
end
function set_fields!(v::Vars2, x::Vector{Float64}, j::Int)
v.r1 = x[j]
v.r2 = x[j+1]
v.r3 = x[j+2]
v.r4 = x[j+3]
v.r5 = x[j+4]
end
function copy_from_x!(vars::Vector{Any}, x::Vector{Float64})::Nothing
j = 1
for v in vars
set_fields!(v, x, j)
j = j+5
end
return nothing;
end
# Build Data structure (vector of Vars1 and Vars2 objects)
function buildVars(nc::Int)
v1 = Vars1(1.0, 2.0, 3.0, 4.0, 5.0)
r1 = Vars2(6.0, 7.0, 8.0, 9.0, 10.0)
vars = Any[]
for i = 1:nc
push!(vars, deepcopy(v1))
push!(vars, deepcopy(r1))
end
return vars
end
nc=2; vars = buildVars(nc); x = randn(nc*10)
println("\nnc = $nc")
copy_from_x!(vars, x)
@time copy_from_x!(vars, x)
nc=10; vars = buildVars(nc); x = randn(nc*10)
println("\nnc = $nc")
@time copy_from_x!(vars, x)
nc=100; vars = buildVars(nc); x = randn(nc*10)
println("\nnc = $nc")
@time copy_from_x!(vars, x)
nc=10000; vars = buildVars(nc); x = randn(nc*10)
println("\nnc = $nc")
@time copy_from_x!(vars, x)
end
The output is:
nc = 2
0.000003 seconds (4 allocations: 160 bytes)
nc = 10
0.000003 seconds (4 allocations: 160 bytes)
nc = 100
0.000005 seconds (4 allocations: 160 bytes)
nc = 10000
0.000184 seconds (4 allocations: 160 bytes)
So this looks good with respect to memory (no unnecessary memory allocated).
However, multiple dispatch is used in a way so that functions set_fields!
are resolved at runtime and it seems not possible to avoid this. The question is whether there will be issues if many different types are used in vector vars
. In this discourse discussion a similar topic seems to be discussed and when I read this correctly, there can be in fact a substantial overhead of multiple dispatch in such a case.