Hello,
I’m trying to use StructArrays.jl as a fast, convenient solution for working with coordinate data. But I’m having difficulty operating on StructArrays in the way I had hoped I could.
Here is a MWE of what I’m trying to do:
# a struct to hold individual coordinates
mutable struct Coordinate{T<:Real}
x::T
y::T
end
Coordinate() = Coordinate(0.0, 0.0)
# a StructArray of coordinates
s = StructArray([Coordinate() for i = 1:10])
10-element StructArray(::Vector{Float64}, ::Vector{Float64}) with eltype Coordinate{Float64}:
Coordinate{Float64}(0.0, 0.0)
Coordinate{Float64}(0.0, 0.0)
Coordinate{Float64}(0.0, 0.0)
Coordinate{Float64}(0.0, 0.0)
Coordinate{Float64}(0.0, 0.0)
Coordinate{Float64}(0.0, 0.0)
Coordinate{Float64}(0.0, 0.0)
Coordinate{Float64}(0.0, 0.0)
Coordinate{Float64}(0.0, 0.0)
Coordinate{Float64}(0.0, 0.0)
On the surface, this gives me exactly what I want - I can easily access individual coordinates and I can rapidly iterate over the whole container. But I’m struggling to mutate the elements of this container without allocation.
While I can easily mutate the component arrays of a StructArray, there are many instances where I would like to operate on a single instance of Coordinate within the larger StructArray. The former is always non-allocating, but the latter is what’s causing me difficulty:
# mutating an element of the component array
@allocated s.x[1] = 1.0
0
# attempting to mutate both elements of a single coordinate
@allocated s[1] = Coordinate(1.0, 1.0)
32
The second result seems to indicate a new instance of Coordinate was allocated, and then copied into the elements of s[1]. If I choose to define Coordinate as an immutable struct instead of a mutable one, this behavior changes and the assignment above no longer allocates. However, using the immutable form prohibits me from writing any functions that operate on single instances of Coordinate. Here is a toy example of such a function:
# randomly increment a value
function step!(coord::Coordinate)
coord.x += rand(-1:1)
coord.y += rand(-1:1)
return nothing
end
@allocated step!(s[1])
32
My intent is to mutate in place, but because the function is written to operate on the struct, a new struct is allocated and the values copied. It’s unclear to me how to write subroutines for StructArrays which are non-allocating.
I’ve developed an alternative solution through the use of StaticArrays.jl, which simply uses MVectors to house the coordinates instead of a custom struct. I can freely pass MVectors to and from functions without allocation, but I lose the convenient syntax of the StructArray and the ability to rapidly iterate over columns.
My goal is to be able to write functions which operate on StructArrays, or individual elements thereof, without allocation. Any suggestions would be greatly appreciated.
Thanks in advance.