This blog post could be useful.
But why not pass this argument into the function as an SMatrix
from the beginning. Everything should be an SArray from the beginning.
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)
)
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.
I love that idea.. a couple of questions if you will. Below is my struct
that stores the lVecs
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?
Can you have two structs, mutable and immutable?
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.
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}
?
I would try replacing the entire struct, with everything in it. The Vectors can be passed by reference, and the StaticArrays can be replaced.
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:
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?
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.
But Mmatrix performs better in this example.
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
:
S = rand(SMatrix{3,3,Float64,9})
(Not sure if all the parameters are needed for performance, test that).
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.
Do you the SetField.jl might be useful for your code?
I believe you mean Accessors.jl
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]
.
Your experiment does not seem meaningful:
-
Itâs comparing apples to oranges, as
modify_smatrix
andmodify_mmatrix!
do different things.modify_mmatrix!
mutates the argument, whilemodify_smatrix
is a no-op. Iâm not talking about the implementation here, just about the semantics. -
As pointed out by DNF, the
SMatrix
function is not implemented well, resulting in unnecessarily bad performance.