# How is memory allocated for a vector of structs?

**URL:** <https://discourse.julialang.org/t/how-is-memory-allocated-for-a-vector-of-structs/115314>\
**Category:** Performance\
**Tags:** question\
**Created:** [June 7, 2024, 8:52am UTC](https://discourse.julialang.org/t/how-is-memory-allocated-for-a-vector-of-structs/115314 "2024-06-07T08:52:30Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![fmario](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fmario/32/209791_2.png) [@fmario](https://discourse.julialang.org/u/fmario)\
**Post date:** [June 7, 2024, 8:52am UTC](https://discourse.julialang.org/t/how-is-memory-allocated-for-a-vector-of-structs/115314/1 "2024-06-07T08:52:30Z")

</div>

Say we have a mutable struct like this:

```julia
mutable struct Foo
    a::Int
    b::Int
end

```

I have to allocate many of these structs. In C, I would allocate them all at once in an array, instead of using `malloc` to allocate each one individually. In Julia, if I create a vector of them, the vector contains pointers to the structs instead of the structs themselves:

```julia
julia> b = Vector{Foo}(undef, 1000)
julia> Base.summarysize(b)
8040

```

If I use a non-mutable struct instead, the size of `b` is 16040, indicating that the vector contains the structs themselves, not pointers to them.

I understand this is happening because of the mutable struct, but can it be changed? Can I allocate all of them at once?

---

<div class="post-metadata">

**Author:** ![greatpet](https://avatars.discourse-cdn.com/v4/letter/g/e495f1/32.png) [@greatpet](https://discourse.julialang.org/u/greatpet)\
**Post date:** [June 7, 2024, 8:59am UTC](https://discourse.julialang.org/t/how-is-memory-allocated-for-a-vector-of-structs/115314/2 "2024-06-07T08:59:20Z")

</div>

I don’t think the behavior can be changed. For immutable objects, you can emulate mutation with Accessors.jl.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [June 7, 2024, 9:37am UTC](https://discourse.julialang.org/t/how-is-memory-allocated-for-a-vector-of-structs/115314/3 "2024-06-07T09:37:33Z")

</div>

This post was temporarily hidden by the community for possibly being off-topic, unfocused, inappropriate, or spammy.

---

<div class="post-metadata">

**Author:** ![fmario](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fmario/32/209791_2.png) [@fmario](https://discourse.julialang.org/u/fmario)\
**Post date:** [June 7, 2024, 9:46am UTC](https://discourse.julialang.org/t/how-is-memory-allocated-for-a-vector-of-structs/115314/4 "2024-06-07T09:46:12Z")

</div>

Thanks. Is there a way to efficiently allocate space for many such structs, or should I just trust Julia will do it well?

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [June 7, 2024, 10:08am UTC](https://discourse.julialang.org/t/how-is-memory-allocated-for-a-vector-of-structs/115314/5 "2024-06-07T10:08:03Z")

</div>

You could in theory `Libc.malloc` the structs yourself & `unsafe_load` the instances, at the cost of having to juggle lifetimes manually, but I wouldn’t recommend it. What’s the point of using a language that models mutability if we’re falling back to manually tracking everything ourselves again?

> [@fmario](#):
>
> s there a way to efficiently allocate space for many such structs, or should I just trust Julia will do it well?

For mutable structs themselves, you’ll (generally) have to trust julia to do well, though the question is if you really need mutable structs in the first place. If they’re immutable and `isbits` (i.e. they don’t contain pointers/mutable data), the struct will be allocated inline in the array.

---

<div class="post-metadata">

**Author:** ![greatpet](https://avatars.discourse-cdn.com/v4/letter/g/e495f1/32.png) [@greatpet](https://discourse.julialang.org/u/greatpet)\
**Post date:** [June 7, 2024, 10:49am UTC](https://discourse.julialang.org/t/how-is-memory-allocated-for-a-vector-of-structs/115314/6 "2024-06-07T10:49:44Z")

</div>

Immutable structs may offer better performance even if you need to update a subset of data fields. See a benchmark here:

```julia
using BenchmarkTools

mutable struct MutFoo
    a::Int
    b::Int
end

function double_b!(v::Vector{MutFoo})
    for w in v
        w.b *= 2
    end
    v
end

struct ImmutFoo
    a::Int
    b::Int
end

function double_b!(v::Vector{ImmutFoo})
    for i in eachindex(v)
        v[i] = ImmutFoo(v[i].a, v[i].b * 2)
    end
    v
end

println("Benchmarking vector of mutable objects")
const mut_vec = [MutFoo(0, 0) for i in 1:10^6]
@btime double_b!(mut_vec)

println("Benchmarking vector of immutable objects")
const immut_vec = [ImmutFoo(0, 0) for i in 1:10^6]
@btime double_b!(immut_vec)

```

Result on my laptop:

```
Benchmarking vector of mutable objects
  2.907 ms (0 allocations: 0 bytes)
Benchmarking vector of immutable objects
  1.164 ms (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)\
**Post date:** [June 7, 2024, 12:26pm UTC](https://discourse.julialang.org/t/how-is-memory-allocated-for-a-vector-of-structs/115314/7 "2024-06-07T12:26:42Z")

</div>

Another approach is

> **[GitHub - JuliaArrays/StructArrays.jl: Efficient implementation of struct...](https://github.com/JuliaArrays/StructArrays.jl)**
>
> Efficient implementation of struct arrays in Julia - JuliaArrays/StructArrays.jl

---

<div class="post-metadata">

**Author:** ![fmario](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fmario/32/209791_2.png) [@fmario](https://discourse.julialang.org/u/fmario)\
**Post date:** [June 7, 2024, 1:25pm UTC](https://discourse.julialang.org/t/how-is-memory-allocated-for-a-vector-of-structs/115314/8 "2024-06-07T13:25:13Z")

</div>

Their approach basically consists in storing the struct’s fields in different arrays. I also tried that. The problem here is that this breaks cache locality, and in my application it really slows the program down. Nice to know though.

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [June 7, 2024, 2:22pm UTC](https://discourse.julialang.org/t/how-is-memory-allocated-for-a-vector-of-structs/115314/9 "2024-06-07T14:22:08Z")

</div>

> [@greatpet](#):
>
> Result on my laptop:
> 
> ```julia
> Benchmarking vector of mutable objects
> 2.907 ms (0 allocations: 0 bytes)
> Benchmarking vector of immutable objects
> 1.164 ms (0 allocations: 0 bytes)
> 
> ```

I get

```julia
julia> @btime double_b!($mut_vec);
  2.071 ms (0 allocations: 0 bytes)

julia> @btime double_b!($immut_vec);
  634.991 μs (0 allocations: 0 bytes)

julia> versioninfo()
Julia Version 1.10.3
Commit 0b4590a550 (2024-04-30 10:59 UTC)
Platform Info:
  OS: Linux (x86_64-redhat-linux)
  CPU: 28 × Intel(R) Core(TM) i9-9940X CPU @ 3.30GHz

```

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [June 7, 2024, 2:31pm UTC](https://discourse.julialang.org/t/how-is-memory-allocated-for-a-vector-of-structs/115314/10 "2024-06-07T14:31:54Z")

</div>

> [@fmario](#):
>
> Their approach basically consists in storing the struct’s fields in different arrays. I also tried that. The problem here is that this breaks cache locality, and in my application it really slows the program down. Nice to know though.

It often improves cache locality and cache efficiency, i.e. whenever you’re only accessing a subset of the fields, no need to memory bandwidth and cache capacity on those fields you aren’t using.  
Also, no need to waste space on padding bytes (ever) with StructArrays.jl.

It also is much more SIMD friendly. E.g., here:

```julia
julia> function double_b!(v::AbstractVector{ImmutFoo})
           @inbounds for i in eachindex(v)
               v[i] = ImmutFoo(v[i].a, v[i].b * 2)
           end
           v
       end
double_b! (generic function with 3 methods)

julia> sa_vec = StructArray(immut_vec);

julia> @btime double_b!($sa_vec);
  264.960 μs (0 allocations: 0 bytes)

julia> @btime $sa_vec.b .*= 2;
  266.485 μs (0 allocations: 0 bytes)

```

It is as fast as working with the `b` vector directly, as that is essentially what happens (the compiler optimizes away the loads and stores to `sa_vec.a`).

So whether it helps or hurts is workload dependent.  
Mostly with big structs where you access all the fields and do not iterate through them quickly, or do iterate through them in random orders so that cache lines can’t get reused, are `StructArray`s likely to be bad for cache.
