I’ve wrapped a C library for use in Julia, and have encountered a performance benefit to using a mutable struct as opposed to a struct; this is not what I would have expected. In short: the C-library usually deals with pointers, while in Julia we may want to map the structs (unsafe_load) to easily access values. To deal with static arrays we can use SVectors or NTuples; if a struct using either method us unsafe_load’d, it is much slower (initially) than using a mutable struct.
Take the following code (tested with both 0.7 and 1.0.1), for example:
using StaticArrays
struct S_SA
f::SVector{1000, Float64}
end
mutable struct M_SA
f::SVector{1000, Float64}
end
struct S_NT
f::NTuple{1000, Float64}
end
mutable struct M_NT
f::NTuple{1000, Float64}
end
makeptr(v::DataType) = convert(Ptr{v}, Libc.malloc(sizeof(v)))
p_s = makeptr(S_SA)
p_m = makeptr(M_SA)
println("unsafe_load for SVector mutable struct:")
@time m = unsafe_load(p_m);
println("unsafe_load for SVector struct:")
@time s = unsafe_load(p_s);
println()
println()
println()
p_ns = makeptr(S_NT)
p_nm = makeptr(M_NT)
println("unsafe_load for NTuple struct:")
@time ns = unsafe_load(p_ns);
println("unsafe_load for NTuple mutable struct:")
@time nm = unsafe_load(p_nm);
which nets the following timings for me:
unsafe_load for SVector mutable struct:
0.002560 seconds (3.03 k allocations: 193.748 KiB)
unsafe_load for SVector struct:
0.310711 seconds (3.03 k allocations: 192.795 KiB)
unsafe_load for NTuple struct:
0.295900 seconds (3.03 k allocations: 192.795 KiB)
unsafe_load for NTuple mutable struct:
0.002644 seconds (3.04 k allocations: 201.889 KiB)
Each time, the mutable struct unsafe_loads faster than the struct. On subsequent unsafe_loads, they are the same speed. The sizes of the C-structs I’m using are larger / more complex (not just static arrays, but other fields as well) than this, and the static struct can take up to 30 seconds to unsafe_load, while the mutable struct is similar to the above.
Does anyone in the community have a recommendation? I find 30+ seconds to be far too long, but would have expected that a struct in this case is the correct Julia thing to do. Suggestions very welcome.