julia> x1 = [rand(Int) for _=1:1000];
julia> x2 = [rand(Int) for _=1:1000];
julia> v1 = [rand(3,3) for _=1:1000];
julia> v2 = [rand(3,3) for _=1:1000];
julia> struct myT
a::Int
b::Any
end
julia> mutable struct myT2
const a::Int
const b::Any
end
julia> using BenchmarkTools
julia> k1s = myT.(x1, v1);
julia> k2s = myT.(x2, v2);
julia> @benchmark $k1s .== $k2s
BenchmarkTools.Trial: 10000 samples with 9 evaluations.
Range (min … max): 2.378 μs … 22.189 μs ┊ GC (min … max): 0.00% … 0.00%
Time (median): 2.856 μs ┊ GC (median): 0.00%
Time (mean ± σ): 2.882 μs ± 552.067 ns ┊ GC (mean ± σ): 0.00% ± 0.00%
▁ █ ▁▁
▂▂▂▃▃▃▃▅▃▅▃▄▃▅█▆▅▅▇█▆██▄▅▃▂▃▂▂▂▃▂▃▃▂▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▁▂ ▃
2.38 μs Histogram: frequency by time 3.86 μs <
Memory estimate: 224 bytes, allocs estimate: 3.
julia> l1s = myT2.(x1, v1);
julia> l2s = myT2.(x2, v2);
julia> @benchmark $l1s .== $l2s
BenchmarkTools.Trial: 10000 samples with 172 evaluations.
Range (min … max): 625.581 ns … 8.904 μs ┊ GC (min … max): 0.00% … 91.64%
Time (median): 687.209 ns ┊ GC (median): 0.00%
Time (mean ± σ): 717.957 ns ± 291.679 ns ┊ GC (mean ± σ): 1.68% ± 4.59%
▃ ▂█▃
████████▅▃▂▂▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▂
626 ns Histogram: frequency by time 1.36 μs <
Memory estimate: 224 bytes, allocs estimate: 3.
If my understanding is correct, this performance difference is because myT2 can directly store a pointer to .b along with .a to form a better memory layout. My question is, can the compiler automatically convert the lower-layer implementation of myT to myT2 in the future?
You didn’t define == for the new types, though. The default == method just forwards to ===:
julia> struct S end
julia> @which S() == S()
==(x, y)
@ Base Base.jl:207
So it comes down to === being cheaper for the mutable struct, because it’s implemented as a simple integer comparison, unlike for the immutable struct, where the === is recursive. This is documented, the doc string of === starts with:
Determine whether x and y are identical, in the sense that no program could distinguish them. First the types of x and y are compared. If those are identical, mutable objects are compared by address in memory and immutable objects (such as numbers) are compared by contents at the bit level.
Regarding the unfortunate default method of ==, here are some Github issues:
The behavior is documented, though, the == doc string starts with: