I want to create a very large array of a custom struct type. However, I am finding that the loop that fills the (pre-allocated array) is requiring a large amount of memory allocations and is very slow as a result.
I assume this is because when I set i’th array entry to MyStruct(), I am first creating MyStruct, assigning it (copying), and then immediately throwing it away. How do I avoid this?
This is a working toy code example showing the problem:
# Example code
using StaticArrays
struct MyStruct
field1::SVector{4, Float32}
field2::SMatrix{2, 2, Float32}
end
function myfill(arrayOfStructs)
for i in eachindex(arrayOfStructs)
arrayOfStructs[i] = MyStruct(
SVector{4, Float32}([i, i, i, i]),
SMatrix{2, 2, Float32}([i i; i i])
)
end
end
arrayOfStructs = Array{MyStruct}(undef, 10_000_000)
@time myfill(arrayOfStructs)
That’s a good catch, but I’ve oversimplified my code too much. Here’s a better example, were I’m trying to copy portions of arrays into a new array-of-structs structure.
Ideally, I would like the call to myfill() to be non-allocating, since I have already allocated space for the array. I am assuming the allocations now arise from the use of the @views?
# Example code
using BenchmarkTools
using StaticArrays
struct MyStruct
field1::SVector{4, Float32}
field2::SMatrix{2, 2, Float32}
end
function myfill(arrayOfStructs, origin4, origin2x2)
for i in eachindex(arrayOfStructs)
arrayOfStructs[i] = MyStruct(
SVector{4, Float32}(@view origin4[i, :]),
SMatrix{2, 2, Float32}(@view origin2x2[i, :, :])
)
end
end
N = 1_000_000
origin4 = rand(Float32, N, 4)
origin2x2 = rand(Float32, N, 2, 2)
arrayOfStructs = Array{MyStruct}(undef, N)
@benchmark myfill($arrayOfStructs, $origin4, $origin2x2) samples=1 evals=1 seconds=20
julia> struct MyStruct
field1::SVector{4, Float32}
field2::SMatrix{2, 2, Float32, 4} # you need to specify the length too as the fourth parameter
end
julia> @btime myfill($arrayOfStructs, $origin4, $origin2x2);
2.484 μs (0 allocations: 0 bytes)