No, immutables are preferred. This is why starting in 0.6 they are called struct
and mutable struct
instead of immutable
and type
as they were in 0.5 and earlier.
I don’t know or understand the details or internals, but those who do have said the compiler has an easier time optimizing structs. That structs of isbits
are also isbits
and allocate no memory when creating them:
julia> struct tuplewrap{N}
floats::NTuple{N,Float64}
ints::NTuple{N,Int}
end
julia> using BenchmarkTools
julia> @benchmark tuplewrap(ntuple(i -> 0.0, Val(20)), ntuple(i -> 0, Val(20)))
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 57.336 ns (0.00% GC)
median time: 57.337 ns (0.00% GC)
mean time: 58.824 ns (0.00% GC)
maximum time: 95.378 ns (0.00% GC)
--------------
samples: 10000
evals/sample: 983
julia> isbits(tuplewrap(ntuple(i -> 0.0, Val(20)), ntuple(i -> 0, Val(20))))
true
are also really convenient.
And you don’t always necessarily want to hold them in an array / RefValue / field of a mutable struct.
But I agree. When you do, it’d be nice for it to be much easier / more convenient to mutate them.
In this case, with
struct A
a::Int64
b::Int64
end
the easiest thing would be to reinterpret a_array from an n
length vector of As to a 2 x n
matrix of Int64.
I do think like that much of this seems transparent, which makes it easier to understand and reason about. structs
go on the stack and can’t be modified, mutable structs
are references, so creating them allocates memory on the heap, but we can modify them.
I did also notice that small arrays in Fortran are stack allocated too (even when they’re of unknown size, with -fstack-arrays
), but you have no problem editing their contents.
On that note, when it comes to editing contents, I found that for Julia code the first thing I have to do is actually extract every single element from the arrays, like (eg, with Base.Cartesian.@nextract
):
U_1 = U[1]
U_2 = U[2]
...
U_n = U[n]
# do work
U[1] = U_1
U[2] = U_2
...
U[n] = U_n
or performance would be crippled. With Fortran, it did not seem to make any difference at all. But I’m guessing all that means is that gfortran was more aggressive about creating temporaries and rearranging my operations/indexes to get the same effect.
Granted, code where you’re constantly mutating things can be ugly.