I have a function type_uint(val::Integer) ::Type{<:Unsigned}
which returns the smallest UInt
type that can still represent val
without overflowing. I use this everywhere in my code, but only at top level.
For example, given l\times m and m\times n sparse matrices X and Y, the product will be l\times n, so the indices/positions will be of type tp=type_uint(l)
and the values/weights will be of type tw=promote_type(twx,twy)
. I pass tp(l), tw(0)
and the indices and values of X and Y to a function that does all the work:
_compute_product(l::tp, ::tw,
Xcolptr::Vector{tPx}, Xrowval::Vector{tpx}, Xnzval::Vector{twx},
Ycolptr::Vector{tPy}, Yrowval::Vector{tpy}, Ynzval::Vector{twy})
where {tp<:Integer, tw, tPx<:Integer, tpx<:Integer, twx, tPy<:Integer, tpy<:Integer, twy}
Of course, the latter calls a bunch of other functions (which are parametrized with constrained types like above) that run in hot loops, optionally in parallel.
My question: Obviously tp(l)::Any
is type unstable. But when I pass it to later functions, its type is a concrete integer. Does type-instability stop at the first function, or does it propagate to other functions? Since later functions do not accept Any
arguments, I thought I was safe from losing performance. Is my thinking correct? When I use @code_warntype
, I get:
MethodInstance for *(::MatCSC{UInt24, UInt24, Float64}, ::MatCSC{UInt24, UInt24, Float64}, ::Type{mtd3}, ::Type{prl1})
from *(X::MatCSC{tPx, tpx, twx}, Y::MatCSC{tPy, tpy, twy}, mtd::Type{<:SLATH.Mtd}, prl::Type{<:SLATH.Prl}) where {tPx, tpx, twx, tPy, tpy, twy} @ SLATH ~/venvs_julia/SLATH/src/SLATH.jl:2317
Static Parameters
tPx = UInt24
tpx = UInt24
twx = Float64
tPy = UInt24
tpy = UInt24
twy = Float64
Arguments
#self#::Core.Const(*)
X::MatCSC{UInt24, UInt24, Float64}
Y::MatCSC{UInt24, UInt24, Float64}
mtd::Core.Const(mtd3)
prl::Core.Const(prl1)
Locals
@_6::Int64
@_7::Int64
ww::Vector{Float64}
pp::Vector
I::Vector{UInt64}
_0::Float64
tp::Type
tP::Type{UInt64}
n::Int64
_m::Int64
m::Int64
l::Int64
@_18::MatCSC{UInt64, T, Float64} where T<:Integer
Body::MatCSC{UInt64, T, Float64} where T<:Integer
1 β Core.NewvarNode(:(@_6))
β Core.NewvarNode(:(ww))
β Core.NewvarNode(:(pp))
β Core.NewvarNode(:(I))
β Core.NewvarNode(:(_0))
β Core.NewvarNode(:(tp))
β Core.NewvarNode(:(tP))
β %8 = SLATH.MatCSC::Core.Const(MatCSC)
β %9 = SLATH.size::Core.Const(size)
β %10 = (%9)(X)::Tuple{Int64, Int64}
β %11 = SLATH.size::Core.Const(size)
β %12 = (%11)(Y)::Tuple{Int64, Int64}
β %13 = Core._apply_iterate(Base.iterate, Core.tuple, %10, %12)::NTuple{4, Int64}
β %14 = Base.indexed_iterate(%13, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
β (l = Core.getfield(%14, 1))
β (@_7 = Core.getfield(%14, 2))
β %17 = @_7::Core.Const(2)
β %18 = Base.indexed_iterate(%13, 2, %17)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(3)])
β (m = Core.getfield(%18, 1))
β (@_7 = Core.getfield(%18, 2))
β %21 = @_7::Core.Const(3)
β %22 = Base.indexed_iterate(%13, 3, %21)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(4)])
β (_m = Core.getfield(%22, 1))
β (@_7 = Core.getfield(%22, 2))
β %25 = @_7::Core.Const(4)
β %26 = Base.indexed_iterate(%13, 4, %25)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(5)])
β (n = Core.getfield(%26, 1))
β %28 = SLATH.:(==)::Core.Const(==)
β %29 = m::Int64
β %30 = _m::Int64
β %31 = (%28)(%29, %30)::Bool
βββ goto #3 if not %31
2 β goto #4
3 β %34 = Base.throw::Core.Const(throw)
β %35 = Base.AssertionError::Core.Const(AssertionError)
β %36 = Main.Base::Core.Const(Base)
β %37 = Base.getproperty(%36, :inferencebarrier)::Any
β %38 = Main.Base::Core.Const(Base)
β %39 = Base.getproperty(%38, :string)::Any
β %40 = (%37)(%39)::Any
β %41 = m::Int64
β %42 = _m::Int64
β %43 = Base.string("X*Y is undefined, since size(X,2) = ", %41, " β ", %42, " = size(Y,1).")::Any
β %44 = (%40)(%43)::Any
β %45 = (%35)(%44)::Any
βββ (%34)(%45)
4 β (tP = SLATH.UInt64)
β %48 = l::Int64
β (tp = SLATH.type_uint(%48))
β %50 = SLATH.:*::Core.Const(*)
β %51 = SLATH.zero::Core.Const(zero)
β %52 = $(Expr(:static_parameter, 3))::Core.Const(Float64)
β %53 = (%51)(%52)::Core.Const(0.0)
β %54 = SLATH.zero::Core.Const(zero)
β %55 = $(Expr(:static_parameter, 6))::Core.Const(Float64)
β %56 = (%54)(%55)::Core.Const(0.0)
β (_0 = (%50)(%53, %56))
β %58 = SLATH.zeros::Core.Const(zeros)
β %59 = tP::Core.Const(UInt64)
β %60 = n::Int64
β %61 = (%60 + 1)::Int64
β (I = (%58)(%59, %61))
β %63 = I::Vector{UInt64}
β Base.setindex!(%63, 1, 1)
β %65 = SLATH._mul_CS_CS!::Core.Const(SLATH._mul_CS_CS!)
β %66 = Base.getproperty(X, :I)::Vector{UInt24}
β %67 = Base.getproperty(X, :pp)::Vector{UInt24}
β %68 = Base.getproperty(X, :ww)::Vector{Float64}
β %69 = Base.getproperty(Y, :I)::Vector{UInt24}
β %70 = Base.getproperty(Y, :pp)::Vector{UInt24}
β %71 = Base.getproperty(Y, :ww)::Vector{Float64}
β %72 = I::Vector{UInt64}
β %73 = tp::Type
β %74 = (%73)(0)::Any
β %75 = _0::Core.Const(0.0)
β %76 = l::Int64
β %77 = m::Int64
β %78 = n::Int64
β %79 = (%65)(mtd, prl, %66, %67, %68, %69, %70, %71, %72, %74, %75, %76, %77, %78)::Tuple{Vector, Vector{Float64}}
β %80 = Base.indexed_iterate(%79, 1)::Core.PartialStruct(Tuple{Vector, Int64}, Any[Vector, Core.Const(2)])
β (pp = Core.getfield(%80, 1))
β (@_6 = Core.getfield(%80, 2))
β %83 = @_6::Core.Const(2)
β %84 = Base.indexed_iterate(%79, 2, %83)::Core.PartialStruct(Tuple{Vector{Float64}, Int64}, Any[Vector{Float64}, Core.Const(3)])
β (ww = Core.getfield(%84, 1))
β %86 = SLATH.MatCSC::Core.Const(MatCSC)
β %87 = tP::Core.Const(UInt64)
β %88 = tp::Type
β %89 = SLATH.typeof::Core.Const(typeof)
β %90 = _0::Core.Const(0.0)
β %91 = (%89)(%90)::Core.Const(Float64)
β %92 = Core.apply_type(%86, %87, %88, %91)::Type{MatCSC{UInt64, T, Float64}} where T
β %93 = tp::Type
β %94 = l::Int64
β %95 = Base.getindex(%93, %94)::Vector
β %96 = I::Vector{UInt64}
β %97 = pp::Vector
β %98 = ww::Vector{Float64}
β %99 = (%92)(%95, %96, %97, %98)::MatCSC{UInt64, T, Float64} where T<:Integer
β (@_18 = %99)
β %101 = @_18::MatCSC{UInt64, T, Float64} where T<:Integer
β %102 = (%101 isa %8)::Core.Const(true)
βββ goto #6 if not %102
5 β goto #7
6 β Core.Const(:(@_18))
β Core.Const(:(Base.convert(%8, %105)))
βββ Core.Const(:(@_18 = Core.typeassert(%106, %8)))
7 β %108 = @_18::MatCSC{UInt64, T, Float64} where T<:Integer
βββ return %108
Is this healthy?
Off-topic, a second question that I have, how can I force julia to specialize in the following case:
function Base.hcat(XX::MatCSC...) @assert length(XX)>=3; ...general implementation ... end
function Base.hcat(X::MatCSC{tP,tp,tw}) where {...} return X end
function Base.hcat(X::MatCSC{tPx,tpx,twx}, Y::MatCSC{tPy,tpy,twy}) where {...} ...more efficient implementation ... end;
When calling on 1 or 2 arguments, I get the assertion error, even though the 1- and 2- case is defined later and is more specific (parametrized by types).