# Why is Julia allocating when accessing these structs?

**URL:** <https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905>\
**Category:** Performance\
**Created:** [November 7, 2022, 8:07pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905 "2022-11-07T20:07:10Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![dellagd](https://avatars.discourse-cdn.com/v4/letter/d/46a35a/32.png) [@dellagd](https://discourse.julialang.org/u/dellagd)\
**Post date:** [November 7, 2022, 8:07pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/1 "2022-11-07T20:07:10Z")

</div>

Been struggling to figure out why accesses to these struct fields are causing memory allocations. Here is my test code:

```julia
module AllocationsTest

struct SubStruct
    a::Float64
    b::Float64
end

struct AllocationsStruct
    arr::Array{SubStruct}
end

function make()
    subStructArr = Array{SubStruct}(undef, 1000)

    for i = eachindex(subStructArr)
        subStructArr[i] = SubStruct(1.0, 2.0)
    end

    return AllocationsStruct(subStructArr)
end

function access(allocStruct::AllocationsStruct)
    return allocStruct.arr[1].a
end

function accessInLoop(allocStruct::AllocationsStruct)
    s = 0
    for i = eachindex(allocStruct.arr)
        s += allocStruct.arr[i].a
    end
    return s
end

end

```

I thought this might require some more type annotation to inform the compiler, but even annotating according to the Julia performance tips docs in as many places as I can think of (and making the code far more verbose in the process), the allocations are still there:

```julia
module AllocationsTest

struct SubStruct{T<:Float64}
    a::T
    b::T
end

struct AllocationsStruct{T<:Array{SubStruct{Float64}}}
    arr::T
end

function make()
    subStructArr = Array{SubStruct{Float64}}(undef, 1000)

    for i = eachindex(subStructArr)
        subStructArr[i] = SubStruct{Float64}(1.0, 2.0)
    end

    return AllocationsStruct{Array{SubStruct{Float64}}}(subStructArr)
end

function access(allocStruct::AllocationsStruct{Array{SubStruct{Float64}}})
    return allocStruct.arr[1].a
end

function accessInLoop(allocStruct::AllocationsStruct{Array{SubStruct{Float64}}})
    s = 0
    for i = eachindex(allocStruct.arr)
        s += allocStruct.arr[i].a
    end
    return s
end

end

```

Creating the struct with `make` and then running the `accessInLoop` function on it results in around 1.5k separate allocations totaling 39kb. Even the single access function causes one 32 byte allocation. Why?

Running on Julia 1.8.2

---

<div class="post-metadata">

**Author:** ![jmair](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jmair/32/35117_2.png) [@jmair](https://discourse.julialang.org/u/jmair)\
**Post date:** [November 7, 2022, 8:17pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/2 "2022-11-07T20:17:34Z")

</div>

I don’t think your function is type stable, causing a box allocation. Here’s a fix:

> [@dellagd](#):
>
> ```julia
> function accessInLoop(allocStruct::AllocationsStruct{Array{SubStruct{T}}}) where {T}
> s = zero(T)
> for i = eachindex(allocStruct.arr)
> s += allocStruct.arr[i].a
> end
> return s
> end
> 
> ```

Also, for now, try the following:

> [@dellagd](#):
>
> ```julia
> struct AllocationsStruct{Q<:SubStruct,T<:AbstractArray{Q}}
> arr::T
> end
> 
> ```

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [November 7, 2022, 8:22pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/3 "2022-11-07T20:22:01Z")

</div>

> [@dellagd](#):
>
> ```julia
> struct AllocationsStruct
> arr::Array{SubStruct}
> end
> 
> ```

`Array{T}` is not a concrete type. `Array{T,1}` (or equivalently `Vector{T}`) is. You are missing this type parameter, so the field is abstractly typed.

In your second, parametric version you almost get there except you write

> [@dellagd](#):
>
> `return AllocationsStruct{Array{SubStruct{Float64}}}(subStructArr)`

If you’d instead written `return AllocationsStruct(subStructArr)`, the type would have been correctly inferred.

But the preceding comment about type stability after `s = 0` also holds.

---

<div class="post-metadata">

**Author:** ![dellagd](https://avatars.discourse-cdn.com/v4/letter/d/46a35a/32.png) [@dellagd](https://discourse.julialang.org/u/dellagd)\
**Post date:** [November 7, 2022, 10:25pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/4 "2022-11-07T22:25:54Z")

</div>

> [@mikmoore](#):
>
> If you’d instead written `return AllocationsStruct(subStructArr)`, the type would have been correctly inferred.

I was able to finally get this example to not allocate on every iteration of the `accessInLoop`

```julia
module AllocationsTest

struct SubStruct{T<:Float64}
    a::T
    b::T
end

struct AllocationsStruct{Q<:SubStruct, T<:Vector{Q}}
    arr::T
end

function make()
    subStructArr = Vector{SubStruct}(undef, 1000)

    for i = eachindex(subStructArr)
        subStructArr[i] = SubStruct(1.0, 2.0)
    end

    return AllocationsStruct{SubStruct{Float64}, Vector{SubStruct{Float64}}}(subStructArr)
end

function access(allocStruct::AllocationsStruct)
    return allocStruct.arr[1].a
end

function accessInLoop(allocStruct::AllocationsStruct)
    s::Float64 = 0
    for i = eachindex(allocStruct.arr)
        s += allocStruct.arr[i].a
    end
    return s
end

end

```

However, I found that doing `return AllocationsStruct{SubStruct{Float64}, Vector{SubStruct{Float64}}}(subStructArr)` in `make` was necessary to make this happen.

In my actual code, I have a large struct with many fields and a variety of types. Is there any way for Julia to infer the correct types without the explicit text duplication of writing it all out in the initialization of the struct when I have told Julia what the types will be when I defined the struct’s fields in the `struct` block?

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [November 7, 2022, 10:46pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/5 "2022-11-07T22:46:25Z")

</div>

> [@dellagd](#):
>
> ```julia
> struct SubStruct{T<:Float64}
> a::T
> b::T
> end
> 
> struct AllocationsStruct{Q<:SubStruct, T<:Vector{Q}}
> arr::T
> end
> 
> ```

There isn’t a benefit to subtyping concrete types (like `Float64`) in type parameters. This would have been much cleaner (and equally performant) as

```julia
struct SubStruct
    a::Float64
    b::Float64
end

struct AllocationsStruct
    arr::Vector{SubStruct}
end

```

and now you don’t need to specify the computed parameters everywhere. Recall that the reason your very first attempt didn’t work well was because you used `Array{SubStruct}` (which is incompletely specified) rather than `Vector{SubStruct}` (which is completely specified) in a struct field.

But I’m suspecting this may not completely answer your questions in your actual use case…

---

<div class="post-metadata">

**Author:** ![dellagd](https://avatars.discourse-cdn.com/v4/letter/d/46a35a/32.png) [@dellagd](https://discourse.julialang.org/u/dellagd)\
**Post date:** [November 8, 2022, 12:20am UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/6 "2022-11-08T00:20:45Z")

</div>

> [@mikmoore](#):
>
> Recall that the reason your very first attempt didn’t work well was because you used `Array{SubStruct}` (which is incompletely specified) rather than `Vector{SubStruct}` (which is completely specified) in a struct field.

Sorry - I guess I conflated that with the second half of your original response. Making that one change does indeed remove all allocations for the `accessInLoop` call. Thank you!

---

<div class="post-metadata">

**Author:** ![alequa](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alequa/32/12338_2.png) [@alequa](https://discourse.julialang.org/u/alequa)\
**Post date:** [June 10, 2024, 12:08pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/7 "2024-06-10T12:08:21Z")

</div>

Hello,

I m having a similar problem with a much simpler struct:

```julia
struct NMDAVoltageDependency{T<: Float32}
		b::T 
		k::T 
		mg::T
end
Mg_mM = 1f0
nmda_b = 3.36f0 #(no unit) parameters for voltage dependence of nmda channels
nmda_k = -0.077f0 #Eyal 2018
NMDA = NMDAVoltageDependency(nmda_b, nmda_k, Mg_mM)
print(@allocated getfield(NMDA,:b)) # 16

using BenchmarkTools
@btime begin 
    a = 0.f0
    for x in 1:1000
        a+=getfield(NMDA,:b)
    end
end
# 40.875 μs (2000 allocations: 31.25 KiB)

const myNMDA = NMDAVoltageDependency(nmda_b, nmda_k, Mg_mM)
@btime begin 
    a = 0.f0
    for x in 1:1000
        a+=getfield(myNMDA,:b)
    end
end
# 1.166 ns (0 allocations: 0 bytes)

```

I don’t understand why/how this is the case. The instance NMDA is immutable and it is being processed within a local scope (the begin). Normally I will pass this to a function, and access it to update some variables of another mutable structs. The access to ALL the field is “boxed” as if the struct was not typestable.

thanks for your help

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [June 10, 2024, 12:25pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/8 "2024-06-10T12:25:32Z")

</div>

NMDA is a non-constant global variable.

---

<div class="post-metadata">

**Author:** ![alequa](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alequa/32/12338_2.png) [@alequa](https://discourse.julialang.org/u/alequa)\
**Post date:** [June 10, 2024, 12:28pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/9 "2024-06-10T12:28:14Z")

</div>

ok, how then I am supposed to make it constant within a scope?

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [June 10, 2024, 12:29pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/10 "2024-06-10T12:29:52Z")

</div>

Pass it as a parameter to a function (or in a let block, use `let NMDA_local = NMDA ... end`).

---

<div class="post-metadata">

**Author:** ![alequa](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alequa/32/12338_2.png) [@alequa](https://discourse.julialang.org/u/alequa)\
**Post date:** [June 10, 2024, 12:37pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/11 "2024-06-10T12:37:54Z")

</div>

It looks the same:

```julia
using BenchmarkTools
struct NMDAVoltageDependency{T<:Float32}
		b::T 
		k::T 
		mg::T
end
Mg_mM = 1f0
nmda_b = 3.36f0 #(no unit) parameters for voltage dependence of nmda channels
nmda_k = -0.077f0 #Eyal 2018
let 
    local_NMDA = NMDAVoltageDependency(nmda_b, nmda_k, Mg_mM)
    @btime for x in 1:1000
            getfield(local_NMDA,:b)
        end
end

## 21.666 μs (1000 allocations: 15.62 KiB)

```

```julia
using BenchmarkTools
struct NMDAVoltageDependency{T<:Float32}
		b::T 
		k::T 
		mg::T
end
Mg_mM = 1f0
nmda_b = 3.36f0 #(no unit) parameters for voltage dependence of nmda channels
nmda_k = -0.077f0 #Eyal 2018
let 
    local_NMDA = NMDAVoltageDependency(nmda_b, nmda_k, Mg_mM)
end
begin 
    @btime for x in 1:1000
        getfield(local_NMDA,:b)
    end
end
## 21.666 μs (1000 allocations: 15.62 KiB)

```

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [June 10, 2024, 12:42pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/12 "2024-06-10T12:42:43Z")

</div>

> [@alequa](#):
>
> `NMDAVoltageDependency(nmda_b, nmda_k, Mg_mM)`

```julia-repl
julia> function f(nmda)
           for i in 1:1000
               getfield(nmda, :b)
           end
       end
f (generic function with 1 method)

julia> nmda = NMDAVoltageDependency(nmda_b, nmda_k, Mg_mM)
NMDAVoltageDependency{Float32}(3.36f0, -0.077f0, 1.0f0)

julia> @btime f($nmda)
  1.637 ns (0 allocations: 0 bytes)

julia> @btime let nmda = $(NMDAVoltageDependency(nmda_b, nmda_k, Mg_mM))
           for i in 1:1000
               getfield(nmda, :b)
           end
       end
  1.693 ns (0 allocations: 0 bytes)

```

Note, though, that since the functions do not do anything, that timing is not really meaningful.

---

<div class="post-metadata">

**Author:** ![alequa](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alequa/32/12338_2.png) [@alequa](https://discourse.julialang.org/u/alequa)\
**Post date:** [June 10, 2024, 1:04pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/13 "2024-06-10T13:04:09Z")

</div>

I am sorry but this code does not solve the actual problem, it works only if you have @btime

@btime expands the macro and define the object as constant within the function scope. But it does not work if I have an actual function, like this:

```julia
using BenchmarkTools
using UnPack
struct NMDAVoltageDependency{T<:Float32}
		b::T 
		k::T 
		mg::T
end
Mg_mM = 1f0
nmda_b = 3.36f0 #(no unit) parameters for voltage dependence of nmda channels
nmda_k = -0.077f0 #Eyal 2018
local_NMDA = let 
    NMDAVoltageDependency(nmda_b, nmda_k, Mg_mM)
end
struct Neuron{VT<:Vector{Float32}, RT<:NMDAVoltageDependency}
    g::VT
    NMDA::RT
end

function update_neuron!(neuron::Neuron, index::Int)
    @unpack g, NMDA = neuron
    g[index] = g[index]*(1-1/NMDA.b) + NMDA.b
end

begin
    # I cannot place $ here or I get this error: "syntax: "$" expression outside quote around"
    nmda = (NMDAVoltageDependency(nmda_b, nmda_k, Mg_mM))
    my_neuron = Neuron(zeros(Float32,1), nmda)
    i = 1
    @btime for _ in 1:1000
        update_neuron!(my_neuron, i)
    end
    # 85.083 μs (3000 allocations: 62.50 KiB)
    my_neuron.g[i] ## 11.25
end

```

In general the interpolation doesn’t seem to me a solution, I just would like to make the object `my_neuron.nmda` type stable.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [June 10, 2024, 1:13pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/14 "2024-06-10T13:13:25Z")

</div>

> [@alequa](#):
>
> ```julia
> my_neuron = Neuron(zeros(Float32,1), nmda)
> i = 1
> @btime for _ in 1:1000
> update_neuron!(my_neuron, i)
> end
> 
> ```

here `my_neuron` and `i` are the non-constant globals. You have to put the complete `for` loop inside a function, and pass it to _that_ function (or let block):

```julia-repl
julia> begin
           # I cannot place $ here or I get this error: "syntax: "$" expression outside quote around"
           nmda = (NMDAVoltageDependency(nmda_b, nmda_k, Mg_mM))
           my_neuron = Neuron(zeros(Float32,1), nmda)
           i = 1
           @btime let my_neuron = $my_neuron, i = $i
               for _ in 1:1000
                   update_neuron!(my_neuron, i)
               end
           end
           # 85.083 μs (3000 allocations: 62.50 KiB)
           my_neuron.g[i] ## 11.25
       end
  4.105 μs (0 allocations: 0 bytes)
11.289598f0

```

---

<div class="post-metadata">

**Author:** ![alequa](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alequa/32/12338_2.png) [@alequa](https://discourse.julialang.org/u/alequa)\
**Post date:** [June 10, 2024, 1:23pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/15 "2024-06-10T13:23:18Z")

</div>

Ok, got it! It works.

```julia
function my_simulation()
    nmda = (NMDAVoltageDependency(nmda_b, nmda_k, Mg_mM))
    my_neuron = Neuron(zeros(Float32,1), nmda)
    i = 1
    for _ in 1:1000
        update_neuron!(my_neuron, i)
    end
    return my_neuron.g[i] ## 11.25
end
@btime my_simulation()
# 2.741 μs (1 allocation: 64 bytes)

```

However, admitted that I have cases in which some variables are forcefully defined outside the simulation loop, for example in another module, or in the global scope, and this variable is an immutable struct (some parameters).

For example:

```julia
nmda = (NMDAVoltageDependency(nmda_b, nmda_k, Mg_mM))

function my_simulation(nmda::NMDAVoltageDependency)
    my_neuron = Neuron(zeros(Float32,1), nmda)
    i = 1
    for _ in 1:1000
        update_neuron!(my_neuron, i)
    end
    return my_neuron.g[i] ## 11.25
end
@btime my_simulation()
# 2.741 μs (1 allocation: 64 bytes)

```

Is there any correct approach to redefine the variable within the simulation scope, such that it is type stable and does not require allocations?

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [June 10, 2024, 1:33pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/16 "2024-06-10T13:33:21Z")

</div>

If the variable is immutable you cannot modify it (you can create a new one and return it from your function, to redefine some variable of the outer scope), mwe:

```julia-repl
julia> i = 1 # immutable
       function f(i)
           i = i + 1
           return i
       end
       
f (generic function with 1 method)

julia> i = f(i) # reassigning global scope i 
2

```

If the variable is mutable, you can mutate it (also, having it passed to the local scope of the function as a parameter):

```julia-repl
julia> x = [1] # mutable
       function f!(x) # this f! mutates x, we use the ! to *indicate* that
           x[1] = x[1] + 1
           return nothing # just to make it different from the previous one
       end
f! (generic function with 1 method)

julia> f!(x)

julia> x
1-element Vector{Int64}:
 2

```

everything there is type-stable.

---

<div class="post-metadata">

**Author:** ![alequa](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alequa/32/12338_2.png) [@alequa](https://discourse.julialang.org/u/alequa)\
**Post date:** [June 10, 2024, 1:40pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/17 "2024-06-10T13:40:37Z")

</div>

Yes, I understand this.

My point is that in a realistic situation, I may have parameters that are declared outside the scope of the simulation loop (the function scope).

Is there any approach to pass these parameters to the function scope that has same performance of when the parameters are defined _inside_ the function scope?

More in general, is there any macro that will spot which variables are defined as non-constant global variables?

---

<div class="post-metadata">

**Author:** ![abraemer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abraemer/32/51403_2.png) [@abraemer](https://discourse.julialang.org/u/abraemer)\
**Post date:** [June 10, 2024, 2:00pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/18 "2024-06-10T14:00:50Z")

</div>

> [@alequa](#):
>
> Is there any approach to pass these parameters to the function scope that has same performance of when the parameters are defined _inside_ the function scope?

Yes, just pass the values as function arguments. Note that it is the variable _binding_ that needs to be local not the _value_. A value is always constant in that sense. Consider:

```julia
i = 5 # global binding, access is type unstable

function foo(i)
    # variable i here refers to the local i
    # access is fast
    ...
end

```

> [@alequa](#):
>
> More in general, is there any macro that will spot which variables are defined as non-constant global variables?

You can use `@code_warntype` to inspect a function call and it will highlight all type instabilities. These always occur when accessing untyped globals but can have other reasons. Regardless, the performance penalty is the same so would want to fix that either way.  
Example:

```julia-repl
julia> foo(x) = n*x
foo (generic function with 1 method)

julia> n=5
5

julia> @code_warntype foo(3)
MethodInstance for foo(::Int64)
  from foo(x) @ Main REPL[1]:1
Arguments
  #self#::Core.Const(foo)
  x::Int64
Body::ANY # these ANY will appear in red!
1 ─ %1 = (Main.n * x)::ANY
└── return %1

```

Here you can see that `n` is the global `n` because it is called `Main.n`.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [June 10, 2024, 2:05pm UTC](https://discourse.julialang.org/t/why-is-julia-allocating-when-accessing-these-structs/89905/19 "2024-06-10T14:05:05Z")

</div>

> [@alequa](#):
>
> Is there any approach to pass these parameters to the function scope that has same performance of when the parameters are defined _inside_ the function scope?

Yes, pass the data as parameters to the function. That’s the point.
