# Reducing allocations

**URL:** <https://discourse.julialang.org/t/reducing-allocations/129415>\
**Category:** New to Julia\
**Tags:** memory-allocation\
**Created:** [May 28, 2025, 4:11pm UTC](https://discourse.julialang.org/t/reducing-allocations/129415 "2025-05-28T16:11:51Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![dqeeq](https://avatars.discourse-cdn.com/v4/letter/d/b9e5f3/32.png) [@dqeeq](https://discourse.julialang.org/u/dqeeq)\
**Post date:** [May 29, 2025, 6:04pm UTC](https://discourse.julialang.org/t/reducing-allocations/129415/21 "2025-05-29T18:04:34Z")

</div>

[This blog post](https://opensourc.es/blog/benchmarking-and-profiling-julia-code/) could be useful.

---

<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:** [May 29, 2025, 6:12pm UTC](https://discourse.julialang.org/t/reducing-allocations/129415/22 "2025-05-29T18:12:55Z")

</div>

> [@lancejnelson](#):
>
> `inv(SMatrix{3,3,Float64,9}(atoms.lVecs))`

But why not pass this argument into the function as an `SMatrix` from the beginning. _Everything_ should be an SArray from the beginning.

---

<div class="post-metadata">

**Author:** ![lancejnelson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lancejnelson/32/21898_2.png) [@lancejnelson](https://discourse.julialang.org/u/lancejnelson)\
**Post date:** [May 29, 2025, 6:16pm UTC](https://discourse.julialang.org/t/reducing-allocations/129415/23 "2025-05-29T18:16:31Z")

</div>

`atoms.lVecs` needs to be an `MMatrix` because the values inside will change. Are you saying that I should convert the MMatrix to an SMatrix before passing it in?

Side question: I notice that converting to an SMatrix /MMatrix can be done in several different ways, some allocate and some do not. What is the preferred way to do it (`@SMatrix`, `SMatrix{3,3,Float64,9}(atoms.lVecs)`)

---

<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:** [May 29, 2025, 6:20pm UTC](https://discourse.julialang.org/t/reducing-allocations/129415/24 "2025-05-29T18:20:26Z")

</div>

Not necessarily. Are you really sure they need to change? Can’t you just replace the matrix instead of updating it? I use StaticArrays.jl a _lot_, but I never use MArrays, because I can always just replace instead of update.

Give it an extra go, maybe you will come to the conclusion that mutation isn’t necessary after all.

---

<div class="post-metadata">

**Author:** ![lancejnelson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lancejnelson/32/21898_2.png) [@lancejnelson](https://discourse.julialang.org/u/lancejnelson)\
**Post date:** [May 29, 2025, 6:24pm UTC](https://discourse.julialang.org/t/reducing-allocations/129415/25 "2025-05-29T18:24:20Z")

</div>

I love that idea.. a couple of questions if you will. Below is my `struct` that stores the `lVecs`

```julia
struct atoms
    title::String
    latpar::Float64
    lVecs::MMatrix{3,3,Float64,9}
    coordSys::Vector{String} # 'D' for Direct or 'C' for cartesian
    positions:: Vector{SVector{3,Float64}} # List of all the atomic basis vector separated by type 
    velocities:: Vector{SVector{3,Float64}} # Velocities of the atoms
    masses:: Vector{Float64}
    atomTypes:: Vector{Int64} # Integers representing the type for each basis atom
    species:: Vector{String} # Species of the atoms 
    energies::MVector{2,Float64}
    lj_vec::Vector{Float64} # Vector of 1/r^6 and 1/r^12 values for each unique pair of atom types
end

```

Since this isn’t a `mutable struct` I won’t be able to replace `lVecs` with a new version. I think the speed associated with working with an immutable struct (albeit with MVectors inside) is greater than working with a mutable struct even though that would allow `lVecs` to be an `SVector`. Am I wrong?

---

<div class="post-metadata">

**Author:** ![dqeeq](https://avatars.discourse-cdn.com/v4/letter/d/b9e5f3/32.png) [@dqeeq](https://discourse.julialang.org/u/dqeeq)\
**Post date:** [May 29, 2025, 6:31pm UTC](https://discourse.julialang.org/t/reducing-allocations/129415/26 "2025-05-29T18:31:40Z")

</div>

Can you have two structs, mutable and immutable?

---

<div class="post-metadata">

**Author:** ![Jake](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jake/32/46007_2.png) [@Jake](https://discourse.julialang.org/u/Jake)\
**Post date:** [May 29, 2025, 7:00pm UTC](https://discourse.julialang.org/t/reducing-allocations/129415/27 "2025-05-29T19:00:45Z")

</div>

I would like to know if your new struct that is not mutable but does have a String, and a couple of Vectors of Strings as well as an unsized Vector of Float64’s will live on the heap. My guess is that the compiler does not know the size of these so it will be on the heap, but I would like someone with much more expertise than I to comment on this.

---

<div class="post-metadata">

**Author:** ![lancejnelson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lancejnelson/32/21898_2.png) [@lancejnelson](https://discourse.julialang.org/u/lancejnelson)\
**Post date:** [May 29, 2025, 7:06pm UTC](https://discourse.julialang.org/t/reducing-allocations/129415/28 "2025-05-29T19:06:03Z")

</div>

Me too, good question. Just some context: `coordSys` really is just a single char, but it will change between two different chars, that’s why i put it into a vector. It felt like a hack but I didn’t see any other way. `species` won’t ever need changing, but can you make an `SVector{2,String}`?

---

<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:** [May 29, 2025, 7:15pm UTC](https://discourse.julialang.org/t/reducing-allocations/129415/29 "2025-05-29T19:15:39Z")

</div>

I would try replacing the entire struct, with everything in it. The Vectors can be passed by reference, and the StaticArrays can be replaced.

---

<div class="post-metadata">

**Author:** ![lancejnelson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lancejnelson/32/21898_2.png) [@lancejnelson](https://discourse.julialang.org/u/lancejnelson)\
**Post date:** [May 31, 2025, 2:07am UTC](https://discourse.julialang.org/t/reducing-allocations/129415/30 "2025-05-31T02:07:02Z")

</div>

That feels awkward to me especially since I would be copying most of the struct fields by reference and could easily make an unintended modification. And I ran a little experiment to compare repeated modifcation of an MMatrix vs repeated creation of an SMatrix:

```julia
using StaticArrays
using BenchmarkTools

# Function to modify MMatrix in-place
function modify_mmatrix!(M::MMatrix{3,3,Float64})
    for _ in 1:1000
        for i in 1:3, j in 1:3
            M[i,j] = rand() # Simulate modification
        end
    end
end

# Function to create new SMatrix each iteration
function modify_smatrix(S::SMatrix{3,3,Float64})
    for _ in 1:1000
        S = SMatrix{3,3}(rand(3,3)) # Simulate replacement
    end
   # return S
end

# Initialize matrices
M = MMatrix{3,3}(rand(3,3))
S = SMatrix{3,3}(rand(3,3))

# Benchmark
println("MMatrix in-place modification:")
@btime modify_mmatrix!($M) #(25 micro seconds, 0 allocations)

println("SMatrix replacement:")
@btime modify_smatrix($S). # (67 microseconds, 2000 allocations)

```

It seems that all those modifications of the MMatrix are “in-place” and hence done on the stack. If I ever want to do matrix multiplication or something I can just convert my MMatrix to an SMatrix first. What say ye?

---

<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:** [May 31, 2025, 2:48am UTC](https://discourse.julialang.org/t/reducing-allocations/129415/31 "2025-05-31T02:48:03Z")

</div>

It’s definitely true that `SMatrix` is friendlier to the compiler optimizer than `MMatrix` (for small-enough size, like here). It’s more transparent to the compiler, making it more likely to get optimized well.

---

<div class="post-metadata">

**Author:** ![lancejnelson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lancejnelson/32/21898_2.png) [@lancejnelson](https://discourse.julialang.org/u/lancejnelson)\
**Post date:** [May 31, 2025, 3:36am UTC](https://discourse.julialang.org/t/reducing-allocations/129415/32 "2025-05-31T03:36:40Z")

</div>

But Mmatrix performs better in this example.

---

<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:** [May 31, 2025, 4:46am UTC](https://discourse.julialang.org/t/reducing-allocations/129415/33 "2025-05-31T04:46:04Z")

</div>

> [@lancejnelson](#):
>
> `S = SMatrix{3,3}(rand(3,3))`

That’s not how to create a random `SMatrix`. In your example you create a regular 3x3 `Array{Float64, 2}` in each iteration, and afterwards you convert it.

Instead, _directly_ create an `SMatrix`:

```julia
S = rand(SMatrix{3,3,Float64,9}) 

```

(Not sure if all the parameters are needed for performance, test that).

---

<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:** [May 31, 2025, 6:13am UTC](https://discourse.julialang.org/t/reducing-allocations/129415/34 "2025-05-31T06:13:55Z")

</div>

Even so, this particular benchmark might not be ideal, since I suspect that the `rand` part might be dominating the runtime, rather than the object creation/update. Also try a more lightweight operation like adding or multiplying with a number.

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [May 31, 2025, 7:33am UTC](https://discourse.julialang.org/t/reducing-allocations/129415/35 "2025-05-31T07:33:19Z")

</div>

Do you the [SetField.jl](https://github.com/jw3126/Setfield.jl) might be useful for your code?

---

<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:** [May 31, 2025, 8:13am UTC](https://discourse.julialang.org/t/reducing-allocations/129415/36 "2025-05-31T08:13:15Z")

</div>

I believe you mean Accessors.jl

---

<div class="post-metadata">

**Author:** ![tverho](https://avatars.discourse-cdn.com/v4/letter/t/839c29/32.png) [@tverho](https://discourse.julialang.org/u/tverho)\
**Post date:** [May 31, 2025, 9:56am UTC](https://discourse.julialang.org/t/reducing-allocations/129415/37 "2025-05-31T09:56:39Z")

</div>

My instinct would be to avoid MMatrix because using them often doesn’t lead to the desired result (no allocations) and their behavior is not easy to reason about. On other hand, I don’t think it’s too important whether `Atoms` is allocated in heap or not as it seems to be a singular object containing large vectors (that will be in the heap in any case). I’d imagine making it mutable should not change much? If you’d rather keep it immutable, one option is to have `lVecs::Ref{SMatrix{3,3,Float64,9}}`, essentially a vector of one.

Fun fact, even the bracket syntax produces an SMatrix with SVector inputs: `new_cell = [new_vec l2 l3]`.

---

<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:** [May 31, 2025, 10:03am UTC](https://discourse.julialang.org/t/reducing-allocations/129415/38 "2025-05-31T10:03:44Z")

</div>

> [@lancejnelson](#):
>
> But Mmatrix performs better in this example.

Your experiment does not seem meaningful:

- It’s comparing apples to oranges, as `modify_smatrix` and `modify_mmatrix!` do different things. `modify_mmatrix!` mutates the argument, while `modify_smatrix` is a no-op. I’m not talking about the implementation here, just about the semantics.

- As pointed out by DNF, `modify_smatrix` is not implemented well, resulting in unnecessarily bad performance.

---

<div class="post-metadata">

**Author:** ![lancejnelson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lancejnelson/32/21898_2.png) [@lancejnelson](https://discourse.julialang.org/u/lancejnelson)\
**Post date:** [May 31, 2025, 1:19pm UTC](https://discourse.julialang.org/t/reducing-allocations/129415/39 "2025-05-31T13:19:28Z")

</div>

Well I think I’ve convinced myself that I can make `atoms` immutable but I’m still somewhat undecided on `MMatrix` or `SMatrix` for lVecs. I tried implementing it as an `SMatrix` but a lot of the routines that use `lVecs` became awkward and confusing because of all of the pass by reference during the copying. For example, here is one of the more important functions that needs to change `lVecs`

```julia
function do_cell_step!(atoms,p_accept,T,E_max,ns_params,model;execute = true,check_energy = true)
    if (p_accept < 1.0) && (rand() > p_accept)
        return false   
    end

    new_cell = T * atoms.lVecs
    newVolume = ase.cell_volume(new_cell * atoms.latpar)
    if newVolume > ns_params.max_volume_per_atom * atoms.nAtoms
        return false
    end
    if ase.min_aspect_ratio(new_cell) < ns_params.min_aspect_ratio
        return false
    end

    if check_energy
        oldCell = copy(atoms.lVecs)
        ase.set_cell!(atoms,new_cell,scale_atoms = true)
        newEnergy = ase.eval_energy(atoms,model,force_recalc = true)
        if newEnergy < E_max
            if execute
                atoms.energies[2] = newEnergy
            else
                ase.set_cell!(atoms,oldCell,scale_atoms = true)
            end
            return true
        else
            ase.set_cell!(atoms,oldCell,scale_atoms = true)
            return false
        end
    else
        if execute
            ase.set_cell!(atoms,new_cell,scale_atoms = true)
        end
        return true
    end

end

```

The function `ase.set_cell!` modifies `atoms.lVecs` and if `lVecs` is an `SMatrix` then I had to redesign it so that the new `atoms` was returned. And this function `do_cell_step` had to be redesigned to return the new `atoms` object. anyway, it became awkward in my opinion..

Any thoughts?

---

<div class="post-metadata">

**Author:** ![lancejnelson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lancejnelson/32/21898_2.png) [@lancejnelson](https://discourse.julialang.org/u/lancejnelson)\
**Post date:** [May 31, 2025, 1:28pm UTC](https://discourse.julialang.org/t/reducing-allocations/129415/40 "2025-05-31T13:28:26Z")

</div>

You’re right. Good Catch..

[Previous page](https://discourse.julialang.org/t/reducing-allocations/129415.md?page=1)

[Next page](https://discourse.julialang.org/t/reducing-allocations/129415.md?page=3)
