# Mutating a StructArray without allocation

**URL:** <https://discourse.julialang.org/t/mutating-a-structarray-without-allocation/121311>\
**Category:** General Usage\
**Tags:** question, package, allocations, structarraysjl\
**Created:** [October 15, 2024, 3:48am UTC](https://discourse.julialang.org/t/mutating-a-structarray-without-allocation/121311 "2024-10-15T03:48:25Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![prconlin](https://avatars.discourse-cdn.com/v4/letter/p/b19c9b/32.png) [@prconlin](https://discourse.julialang.org/u/prconlin)\
**Post date:** [October 15, 2024, 3:48am UTC](https://discourse.julialang.org/t/mutating-a-structarray-without-allocation/121311/1 "2024-10-15T03:48:25Z")

</div>

Hello,

I’m trying to use [StructArrays.jl](https://juliaarrays.github.io/StructArrays.jl/stable/) 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:

```julia
# a struct to hold individual coordinates
mutable struct Coordinate{T<:Real}
    x::T
    y::T
end
Coordinate() = Coordinate(0.0, 0.0)

```

```julia
# 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:

```julia
# mutating an element of the component array
@allocated s.x[1] = 1.0
0

```

```julia
# 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:

```julia
# randomly increment a value
function step!(coord::Coordinate)
    coord.x += rand(-1:1)
    coord.y += rand(-1:1)
    return nothing
end

```

```julia
@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](https://juliaarrays.github.io/StaticArrays.jl/stable/), 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.

---

<div class="post-metadata">

**Author:** ![Sevi](https://avatars.discourse-cdn.com/v4/letter/s/c67d28/32.png) [@Sevi](https://discourse.julialang.org/u/Sevi)\
**Post date:** [October 15, 2024, 7:02am UTC](https://discourse.julialang.org/t/mutating-a-structarray-without-allocation/121311/2 "2024-10-15T07:02:25Z")

</div>

Hi!

I haven’t used the package before, but it sounds like you are running into this issue?  
[https://juliaarrays.github.io/StructArrays.jl/stable/counterintuitive/](https://juliaarrays.github.io/StructArrays.jl/stable/counterintuitive/)

Considering this statement from the page,

> A StructArray with immutable elements will in many cases behave identically to (but be more efficient than) a StructArray with mutable elements.

it does sound like using the immutable structs would avoid the issues. You could for example rewrite the method(s) operating on the structs to return a new instance:

```julia
function step(coord::Coordinate)
    return Coordinate(coord.x + rand(-1:1), coord.y + rand(-1:1))
end

function step_array!(s)
    for i in eachindex(s)
        s[i] = step(s[i])
    end
    return s
end

```

(Actually, the `step!` function in this example also doesn’t allocate with the mutable version of the struct either.)

Just an aside: Have you checked if you actually have a benefit of StructArrays.jl compared to a normal `Array` of your structs? I’m not sure if you will see much performance benefit when operating on single elements – it sounds like you want to rewrite your operations to operate on every field of each struct at once, since the fields are just stored as vectors internally.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [October 15, 2024, 11:38am UTC](https://discourse.julialang.org/t/mutating-a-structarray-without-allocation/121311/3 "2024-10-15T11:38:25Z")

</div>

Fully support the advice to use immutable structs and functions like `step(coord::Coordinate)::Coordinate` instead of mutables. Tend to be faster even outside of the StructArrays context, and can be cleaner as well.

---

<div class="post-metadata">

**Author:** ![prconlin](https://avatars.discourse-cdn.com/v4/letter/p/b19c9b/32.png) [@prconlin](https://discourse.julialang.org/u/prconlin)\
**Post date:** [October 15, 2024, 2:12pm UTC](https://discourse.julialang.org/t/mutating-a-structarray-without-allocation/121311/4 "2024-10-15T14:12:07Z")

</div>

Thanks for the input.  
Using immutable structs allows me to directly operate on the StructArray without allocation, but doesn’t solve the problem with functions.  
Adjusting my definitions like so:

```julia
# the struct is now immutable
struct Coordinate{T<:Real}
    x::T
    y::T
end
Coordinate() = Coordinate(0.0, 0.0)

```

```julia
# step function now returns values instead of mutating in place
function step(coord::Coordinate)
    return Coordinate(coord.x + rand(-1:1), coord.y + rand(-1:1))
end

```

I then define the StructArray `s` as I did in the original post.

```julia
# Assignment mutates the component arrays (good)
@allocated s[1] = Coordinate(1.0, 1.0)
0

```

```julia
# functions still allocate and copy values (bad)
@allocated s[1] = step(s[1])
64

```

I also can’t define functions designed to mutate the struct itself, as the struct is no longer mutable. **I suspect the solution has to involve operating on the component arrays of the StructArray. But I can’t figure out how to adjust my functions to make that happen.**

Also, regarding your aside @Sevi:  
I haven’t gotten to the point of benchmarking the performance of a StructArray yet, but they allow me to vectorize operations over columns should I choose to do so. This is not possible with an Array of structs. I think the problem may be that I want to have my cake and eat it, too - the ability to operate on single instance of coord, and also rapidly iterate over columns. My solution using StaticVectors is completely allocation-free, but forces me to give up one of those two operations.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [October 15, 2024, 2:55pm UTC](https://discourse.julialang.org/t/mutating-a-structarray-without-allocation/121311/5 "2024-10-15T14:55:42Z")

</div>

> [@prconlin](#):
>
> ```julia
> # functions still allocate and copy values (bad)
> @allocated s[1] = step(s[1])
> 64
> 
> ```

Looks like the classical “benchmarking in the global scope” issue (:  
I don’t see any allocations when putting this code into a function:

```julia
julia> function f(s)
           s[1] = step(s[1])
       end

julia> @btime f($s)
  5.916 ns (0 allocations: 0 bytes)

```

> [@prconlin](#):
>
> I also can’t define functions designed to mutate the struct itself, as the struct is no longer mutable.

Try viewing this as a positive and write code without struct mutations 🙂

---

<div class="post-metadata">

**Author:** ![prconlin](https://avatars.discourse-cdn.com/v4/letter/p/b19c9b/32.png) [@prconlin](https://discourse.julialang.org/u/prconlin)\
**Post date:** [October 16, 2024, 1:44pm UTC](https://discourse.julialang.org/t/mutating-a-structarray-without-allocation/121311/6 "2024-10-16T13:44:26Z")

</div>

You’re exactly right.

I probably could have saved us all some time if I’d been more careful about how I use `@allocated`.  
So it seems the solution is simply to switch to immutable structs.

Thanks again
