Allocations when assigning Static Array in mutable struct

Hi,
I encountered some allocations in my code that I can pin down to the following foobar example.
When assigning a static array to a variable in a struct i get allocations, which I don’t get when the static array gets allocated to a variable outside of a struct.

julia> mutable struct Foo
           m ::SMatrix{3,2,Float64,6}
           v ::SVector{3,Float64}
       end

julia> bar = Foo([1.,2,3,4,5,6], [1.,2,3])
Foo([1.0 4.0; 2.0 5.0; 3.0 6.0], [1.0, 2.0, 3.0])

julia> @benchmark bar.m = f()
BenchmarkTools.Trial: 10000 samples with 973 evaluations.
 Range (min … max):  66.960 ns …   3.199 ΞΌs  β”Š GC (min … max): 0.00% … 96.51%
 Time  (median):     73.921 ns               β”Š GC (median):    0.00%
 Time  (mean Β± Οƒ):   83.220 ns Β± 132.248 ns  β”Š GC (mean Β± Οƒ):  8.16% Β±  5.00%

    β–β–ˆβ–†β–†β–„β–‚
  β–ƒβ–…β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–…β–ƒβ–ƒβ–ƒβ–ƒβ–‚β–‚β–‚β–ƒβ–ƒβ–ƒβ–ƒβ–‚β–‚β–‚β–‚β–‚β–‚β–‚β–‚β–‚β–‚β–‚β–β–‚β–‚β–β–‚β–‚β–β–β–‚β–‚β–‚β–β–β–β–‚β–β–β–β–‚β–‚β–‚β–‚β–‚β–‚β–‚β–‚β–‚β–‚β–‚ β–ƒ
  67 ns           Histogram: frequency by time          145 ns <

 Memory estimate: 128 bytes, allocs estimate: 2.

julia> @benchmark m = f()
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
 Range (min … max):  0.025 ns … 0.265 ns  β”Š GC (min … max): 0.00% … 0.00%
 Time  (median):     0.027 ns             β”Š GC (median):    0.00%
 Time  (mean Β± Οƒ):   0.027 ns Β± 0.003 ns  β”Š GC (mean Β± Οƒ):  0.00% Β± 0.00%

             β–‚          β–ˆ           ▁
  β–‚β–β–β–β–β–β–β–β–β–β–β–ˆβ–β–β–β–β–β–β–β–β–β–β–ˆβ–β–β–β–β–β–β–β–β–β–β–β–ˆβ–β–β–β–β–β–β–β–β–β–β–„β–β–β–β–β–β–β–β–β–β–β–‚ β–‚
  0.025 ns       Histogram: frequency by time       0.03 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

julia> g() = SVector(1.,2.,3.)
g (generic function with 1 method)
julia> @benchmark bar.v = g()
BenchmarkTools.Trial: 10000 samples with 963 evaluations.
 Range (min … max):  60.506 ns …  2.470 ΞΌs  β”Š GC (min … max): 0.00% … 95.78%
 Time  (median):     67.485 ns              β”Š GC (median):    0.00%
 Time  (mean Β± Οƒ):   73.114 ns Β± 81.647 ns  β”Š GC (mean Β± Οƒ):  3.95% Β±  3.46%

  β–†β–‡β–…β–ƒβ–‡β–ˆβ–‡β–ˆβ–†β–‡β–‡β–†β–†β–…β–„β–ƒβ–‚β–‚β–β–β–β–β–     ▂▁▁       ▁▃▂▁▂▂   ▁            β–ƒ
  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‡β–‡β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‡β–†β–†β–β–ƒβ–†β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‡β–ˆβ–ˆβ–‡β–†β–†β–‡β–ˆβ–‡β–…β–†β–‡β–‡β–‡ β–ˆ
  60.5 ns      Histogram: log(frequency) by time       115 ns <

 Memory estimate: 64 bytes, allocs estimate: 2.
julia> @benchmark v = g()
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
 Range (min … max):  0.024 ns … 0.049 ns  β”Š GC (min … max): 0.00% … 0.00%
 Time  (median):     0.027 ns             β”Š GC (median):    0.00%
 Time  (mean Β± Οƒ):   0.027 ns Β± 0.001 ns  β”Š GC (mean Β± Οƒ):  0.00% Β± 0.00%

                    β–…         β–ˆ
  β–‚β–β–β–β–β–β–β–β–β–„β–β–β–β–β–β–β–β–β–ˆβ–β–β–β–β–β–β–β–β–β–ˆβ–β–β–β–β–β–β–β–β–β–ˆβ–β–β–β–β–β–β–β–β–ƒβ–β–β–β–β–β–β–β–β–‚ β–‚
  0.024 ns       Histogram: frequency by time       0.03 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

Can you explain me where the allocation come from and how I can get rid off them?

Thank you,
Nico

Probably these are benchmarking artifacts because you are not interpolating the variables:

julia> mutable struct Foo
           m ::SMatrix{3,2,Float64,6}
           v ::SVector{3,Float64}
       end

julia> foo = Foo(rand(SMatrix{3,2,Float64,6}),rand(SVector{3,Float64}))
Foo([0.8724518857438199 0.3632403606484935; 0.8892847298840123 0.12143759712364588; 0.7082712449691183 0.7665415510363147], [0.1758060685606172, 0.7266211355083249, 0.33830992545256433])

julia> @btime foo.v = x  # wrong
  47.284 ns (2 allocations: 64 bytes)
3-element SVector{3, Float64} with indices SOneTo(3):
 0.982401257786785
 0.9164914648537852
 0.5761051073188066

julia> @btime $(foo.v) = $x  # correct
  1.640 ns (0 allocations: 0 bytes)
3-element SVector{3, Float64} with indices SOneTo(3):
 0.982401257786785
 0.9164914648537852
 0.5761051073188066

julia> f() = @SVector [1., 2., 3.];

julia> @btime $(foo.v) = f()
  0.022 ns (0 allocations: 0 bytes)
3-element SVector{3, Float64} with indices SOneTo(3):
 1.0
 2.0
 3.0


3 Likes