Performance: mutable vs immutable structs

performance
memory
memory-allocation

#1

In my code, I need to create a struct to hold two vectors. In terms of speed and memory allocations, would it make a difference if I make it mutable instead of immutable, assuming that there’s no need to make to mutable?


#2

Presumably you are trying to profile it? :wink:


#3

Using immutables seems to make aliasing analysis slightly easier for the compiler, see some analysis in e.g. https://github.com/JuliaLang/julia/pull/16371#issuecomment-219644197.


#4

I don’t know all the details, but in general I would expect to see a much bigger difference between bits types and non-bits types than immutables vs. mutables. A bits type is a primitive or an immutable struct whose fields are all bits types. You can test that with isbits(). Notably, bits types can be stack-allocated, so you can construct them at will with little to no speed or allocation penalty. Here’s a simple demo:

julia> mutable struct MyMutable
         x::Vector{Int}
       end

julia> struct MyImmutable
         x::Vector{Int}
       end

julia> using StaticArrays: SVector

julia> struct MyBits
         x::SVector{2, Int}
       end

Vector is not a bits type, so both MyMutable and MyImmutable are not bits types. But StaticArrays.SVector{2, Int} is a bits type (because it’s immutable and contains only a tuple of two integers), so MyBits is also a bits type. This has a significant performance impact in code that constructs a lot of objects:

julia> using BenchmarkTools

julia> function f_mutable()
         for i in 1:1000
           x = MyMutable([rand(Int), rand(Int)])
       end
       end
f_mutable (generic function with 1 method)

julia> function f_immutable()
         for i in 1:1000
           x = MyImmutable([rand(Int), rand(Int)])
       end
       end
f_immutable (generic function with 1 method)

julia> function f_bits()
         for i in 1:1000
           x = MyBits(SVector(rand(Int), rand(Int)))
         end
       end
f_bits (generic function with 1 method)

julia> @btime f_mutable()
  44.277 μs (3000 allocations: 125.00 KiB)

julia> @btime f_immutable()
  44.220 μs (3000 allocations: 125.00 KiB)

julia> @btime f_bits()
  13.201 μs (0 allocations: 0 bytes)

#5

True but the question was specifically about: