# Question Julia best practices for structs of numbers

**URL:** https://discourse.julialang.org/t/question-julia-best-practices-for-structs-of-numbers/109164
**Category:** General Usage
**Tags:** performance, struct
**Created:** [January 23, 2024, 5:18pm UTC](https://discourse.julialang.org/t/question-julia-best-practices-for-structs-of-numbers/109164 "2024-01-23T17:18:07Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![Devetak](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/devetak/32/50611_2.png) [@Devetak](https://discourse.julialang.org/u/Devetak)
#### Post date: [January 23, 2024, 5:18pm UTC](https://discourse.julialang.org/t/question-julia-best-practices-for-structs-of-numbers/109164/1 "2024-01-23T17:18:07Z")

</div>

Hello. As the title suggests, I have a struct of the form:

```julia
struct Foo
    arr
    int
end

```

Of course, then:

```julia
myFoo = Foo(rand(5), 2)
myFoo.int = 3

```

This code errors. However, I can still do `myFoo.arr .= rand(5)`. Suppose I want to have the possibility of changing `int`. Also, suppose that I really care about performance. I came up with two options, both of which seem suboptimal.

Option 1:

```julia
mutable struct Foo2
    const arr
    int
end
myFoo2 = Foo2(rand(5), 2)

```

Why I don’t like it: It declares a mutable struct, which might lead to possible performance loss.

Option 2:

```julia
myFoo3 = Foo(rand(5), Ref(2))

```

Why I don’t like it: The syntax `myFoo3.int[]` is ugly and hard to read.

For reference:

```julia
using BenchmarkTools

@btime for i in 1:10000
    myFoo2.int = i
end
# 937.456 μs (28467 allocations: 444.80 KiB)

@btime for i in 1:10000
    myFoo3.int[] = i
end
# 573.724 μs (9489 allocations: 148.27 KiB)

```

So, the second option seems much more performative. Am I missing anything?

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [January 23, 2024, 6:06pm UTC](https://discourse.julialang.org/t/question-julia-best-practices-for-structs-of-numbers/109164/2 "2024-01-23T18:06:14Z")

</div>

> [@Devetak](#):
>
> Why I don’t like it: It declares a mutable struct, which might lead to possible performance loss.

There seems to be a contradiction here. You want to mutate the object, but you don’t want it to be mutable? The reason immutable objects can be faster is exactly because they are guaranteed to not be mutated.

You can mutate an array that is contained in an immutable struct because you are not really mutating the parent object.

If you care about performance, you should do two things, add types to the struct fields, and don’t do benchmarking in global scope.

```julia
mutable struct Foo2
    const arr::Vector{Float64}
    int::Int
end

function mybench(N)
    myFoo2 = Foo2(rand(5), 2)
    for i in 1:N
        myFoo2.int = i
    end
    return myFoo2
end

@btime mybench(10_000)

```

---

<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: [January 23, 2024, 6:10pm UTC](https://discourse.julialang.org/t/question-julia-best-practices-for-structs-of-numbers/109164/3 "2024-01-23T18:10:01Z")

</div>

The above said, I don´t think you’ll get a measurable performance difference by declaring `const` an array, which is by itself mutable. The only thing constant there is the reference to the array, in this case.

---

<div class="post-metadata">

### Author: ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)
#### Post date: [January 23, 2024, 6:20pm UTC](https://discourse.julialang.org/t/question-julia-best-practices-for-structs-of-numbers/109164/4 "2024-01-23T18:20:11Z")

</div>

Just keep the struct immutable and use `Accessors`:

```julia
struct Foo{U,V}
    arr::U
    int::V
end

myFoo = Foo(rand(5), 2)

using Accessors

function mybench(myFoo)
    for i in 1:10_000
        @reset myFoo.int = i
    end
    myFoo
end

@btime mybench($myFoo)

```

As noted by @DNF already, having concretely typed fields in the struct is most important for performance!

---

<div class="post-metadata">

### Author: ![Devetak](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/devetak/32/50611_2.png) [@Devetak](https://discourse.julialang.org/u/Devetak)
#### Post date: [January 23, 2024, 7:10pm UTC](https://discourse.julialang.org/t/question-julia-best-practices-for-structs-of-numbers/109164/5 "2024-01-23T19:10:45Z")

</div>

Thank you all for replying. Of course I should benchmark outside of global scope!

Just a follow up. If I expect for `int` to change quite often, would it then be better to declare a mutable struct? Is there no difference in that case?

Also @bertschi the `@reset` macro seems to make a copy so it would be quite wasteful (and would require writing `myFoo = @reset myFoo.int = i` per the documentation.

Yes I know its a bit of an odd request to care about performance while mutating data. Its just that I don’t like to have different templates if I have only Arrays Vs if I need to add a number to them for whichever reason.

Thanks a lot you were super helpful!

---

<div class="post-metadata">

### Author: ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)
#### Post date: [January 23, 2024, 7:26pm UTC](https://discourse.julialang.org/t/question-julia-best-practices-for-structs-of-numbers/109164/6 "2024-01-23T19:26:23Z")

</div>

> [@Devetak](#):
>
> Also @bertschi the `@reset` macro seems to make a copy so it would be quite wasteful (and would require writing `myFoo = @reset myFoo.int = i` per the documentation.

Yes, it does make a (shallow) copy, but immutable structs can usually be stack allocated so this is very cheap and can even save allocations compared to mutable structs.  
Further, `@reset myFoo.int = i` expands into `myFoo = @set myFoo.int = i`, so you don’t need to reassign `myFoo`.

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [January 23, 2024, 8:41pm UTC](https://discourse.julialang.org/t/question-julia-best-practices-for-structs-of-numbers/109164/7 "2024-01-23T20:41:17Z")

</div>

> [@Devetak](#):
>
> If I expect for `int` to change quite often, would it then be better to declare a mutable struct?

If you expect a field to change, the natural solution is to make the struct mutable. But sometimes, for small structs you should consider just making a new object. It depends on how you need to use them, whether mutation is _required_, or just an option.

> [@Devetak](#):
>
> Yes I know its a bit of an odd request to care about performance while mutating data.

Oh, not at all! Often mutation is _more_ efficient. Mutating an existing array is normally more efficient than allocating a new array, and even more so for large arrays. Mutating is common in performance oriented code.

---

<div class="post-metadata">

### Author: ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)
#### Post date: [January 23, 2024, 9:59pm UTC](https://discourse.julialang.org/t/question-julia-best-practices-for-structs-of-numbers/109164/8 "2024-01-23T21:59:21Z")

</div>

If the only problem is the “ugly syntax”, then you could use the following approach of redefining `getproperty` and `setproperty!`. Also, I recommend parameterizing your fields.

```julia-repl
julia> struct Foo{A,I}
           arr::A
           int::I
       end

julia> foo = Foo(rand(5), Ref(2))
Foo{Vector{Float64}, Base.RefValue{Int64}}([0.5315736776442587, 0.36287874566069844, 0.6723523160756516, 0.07312792795028544, 0.46898940828968805], Base.RefValue{Int64}(2))

julia> function Base.getproperty(foo::Foo{<:Any, <: Base.RefValue}, s::Symbol)
           if s == :int
               return getfield(foo, :int)[]
           end
           return getfield(foo, s)
       end

julia> function Base.setproperty!(foo::Foo{<:Any, <: Base.RefValue}, s::Symbol, v)
           if s == :int
               return getfield(foo, :int)[] = v
           end
           return setfield!(foo, s, v)
       end

julia> foo = Foo(rand(5), Ref(2))
Foo{Vector{Float64}, Base.RefValue{Int64}}([0.7603341321695618, 0.006355782041778335, 0.436192674476951, 0.9048628450681871, 0.828408074058733], Base.RefValue{Int64}(2))

julia> foo.int = 5
5

julia> foo.int
5

julia> foo.int = 3
3

julia> foo.int
3

julia> ismutable(foo)
false

```
