Allocations while using static arrays in a struct

It looks like Matrix operations while using StaticArrays in a struct is causing allocations. I am using StaticArrays.jl for Julia 0.6. I created a foobar example to illustrate what I encountered. Hopefully, someone can shed some light on what is happening and how to make the code allocation free.

using StaticArrays

struct Foo{N,T}
    x::SMatrix{N,N,T}
    y::SVector{N,T}
    Foo{N,T}(x::SMatrix{N,N,T}, y::SVector{N,T}) where {N,T} = new(x, y)
end

function mult{N,T}(x::Foo{N,T})
    z = x.x * x.y
    z
end

function measure_mult(x)
    @allocated mult(x)
end

function profilemult()
    x = SMatrix{2,2,Float32}(rand(2,2))
    y = SVector{2,Float32}(rand(2))

    f = Foo{2,Float32}(x,y)
    println("mult", '\t', measure_mult(f))
end

profilemult() # prints "mult    32"

function mult1{N,T}(x::SMatrix{N,N,T}, y::SVector{N,T})
    z = x * y
    z
end

function measure_mult1(x, y)
    @allocated mult1(x, y)
end

function profilemult1()
    x = SMatrix{2,2,Float32}(rand(2,2))
    y = SVector{2,Float32}(rand(2))
    
    println("mult1", '\t', measure_mult1(x,y))
end

profilemult1() # prints "mult1    0"

I am new to Julia but using @code_warntype on mult seems to indicate some kind of type stability.

Variables:
  #self# <optimized out>
  x::Foo{2,Float32}
  z::Any

Body:
  begin
      SSAValue(0) = (Core.getfield)(x::Foo{2,Float32}, :x)::StaticArrays.SArray{Tuple{2,2},Float32,2,L} where L
      z::Any = (StaticArrays._mul)((StaticArrays.Size)(SSAValue(0))::StaticArrays.Size{(2, 2)}, $(QuoteNode(Size(2,))), SSAValue(0), (Core.getfield)(x::Foo{2,Float32}, :y)::StaticArrays.SArray{Tuple{2},Float32,1,2})::Any # line 99:
      return z::Any
  end::Any

Any inputs are appreciated. I am working on an application that requires to keep state using static arrays which cannot have any allocations.

1 Like

The problem is that your SMatrix ist nof fully typed. The correct type is SMatrix{N,M,T,L} where L=M*N. The problem is that we currently cannot do arithmetic in the type domain. Therefore you have to manually specify the total number of entries in your matrix.

So changing things to (I also think that you don’t need the inner constructor)

struct Foo{N,T, L}
    x::SMatrix{N,N,T, L}
    y::SVector{N,T}
end

and fixing things at the other places should resolve your problems.

3 Likes

In situations like this you can check whether your types are concrete, with isconcretetype:

julia> isconcretetype(SMatrix{3,3,Float64})
false

julia> isconcretetype(SMatrix{3,3,Float64,9})
true

julia> isconcretetype(SVector{3,Float64})
true
3 Likes

Thanks. That fixed allocations.