I’m writing a type to represent an affine transformation (y=Ax+b), using static arrays. Here’s the code
using BenchmarkTools
using StaticArrays
struct Affine{F, T, R}
matrix :: SMatrix{T, F, R}
vector :: SVector{T, R}
end
function mkaffine(::Val{T}) where T
mx = zero(SMatrix{T,T,Int})
vec = zero(SVector{T,Int})
Affine(mx, vec) # option 1
# (mx, vec) # option 2
end
function test()
@btime mkaffine(Val(3))
end
test()
The mkaffine()
function has been simplified from my actual use case. The Affine
constructor is type stable, the struct has no fields with non-concrete types, everything involved is immutable and should be able to live fully on the stack. Yet I get allocations. Why?
If you comment out Affine(mx, vec)
and return just (mx, vec)
instead, the allocations go away. The two types should have exactly the same memory layout though.
Variant one:
Body::Affine{3,3,Int64}
1 ─ %1 = %new(Affine{3,3,Int64}, $(QuoteNode([0 0 0; 0 0 0; 0 0 0])), $(QuoteNode([0, 0, 0])))::Affine{3,3,Int64}
└── return %1
Variant 2:
Body::Tuple{SArray{Tuple{3,3},Int64,2,9},SArray{Tuple{3},Int64,1,3}}
1 ─ return ([0 0 0; 0 0 0; 0 0 0], [0, 0, 0])