Weird memory allocation

The allocation occurs for the simplified code posted in the
gist above even if the called function is defined as

function Jacobianvolume{T<:FESet2Manifold}(self::FEMMBase{T}, J::FFltMat,
            loc::FFltMat, conn::FIntMat, N::FFltMat)::FFlt
            return 1.0
end

The expansion is

Variables:
  #self#::FEMMBaseModule.#Jacobianvolume
  self::FEMMBaseModule.FEMMBase{FESetModule.FESetT3,FEMMBaseModule.#otherdimensionunity}
  J::Array{Float64,2}
  loc::Array{Float64,2}
  conn::Array{Int64,2}
  N::Array{Float64,2}

Body:
  begin 
      return 1.0
  end::Float64

Dispatch doesn’t propagate type info for keywords, so FESetT3 is typed as Any.
Here is the problematic code:

type FESetT3 <: FESet2Manifold
  nfensperfe::FInt
  conn::FIntMat
  label::FIntVec

  function  FESetT3(; conn::FIntMat=[], label =zero(FInt))
    const nfensperfe::FInt  = 3
    @assert (size(conn, 2) == nfensperfe) "Number of nodes per element mismatched"
    # Needs to make a COPY of the input arrays
    self = new(nfensperfe, deepcopy(conn), deepcopy(FInt[]))
    return self
  end
end

This is faster:

  function  FESetT3( conn::FIntMat=[], label =zero(FInt))
# etc.

I leave it for the experts to say whether the inference engine is working properly here. Thanks for the puzzle of the day.

2 Likes

Thank you very much dear Sir, this was bugging me no end.

But, how did you figure it out? Where could you see that there was a type instability? What did you do? Or was there something that bit you too?

Petr

This sort of thing has bit me before - keyword arguments are often tricky. In this case, profiling showed that the time was spent in the loop construct itself rather than the function called there, so I applied code_warntype to run1(), which showed the untyped argument femm in the Jacobianvolume call (hence the expensive logic), preceded by

      fes::Any = $(Expr(:invoke, MethodInstance for (::Core.#kw#Type)(::Array{Any,1}, ::Type{FESetModule.FESetT3}), :($(QuoteNode(Type))), :($(Expr(:invoke, MethodInstance for vector_any(::Any, ::Vararg{Any,N} where N), :(Base.vector_any), :(:conn), :(conn)))), :(Driver.FESetT3))) # line 13:
      femm::Any = (Driver.FEMMBase)(fes::Any)::Union{FEMMBaseModule.FEMMBase, FEMMBaseModule.FEMMBase{_,FEMMBaseModule.#otherdimensionunity} where _} # line 14:

which points to the constructor I am quoting in the edited version of my reply above.

@Ralph_Smith:

Much obliged. Thanks!

Petr