Breaking up the second test into a function to make it clearer:
test2(a_vals, b_vals, c_vars, c_card) = MyFactor{c_vars, c_card, Array{Float64, length(c_vars)}}(a_vals .* b_vals)
@btime test2($a_vals, $b_vals, $c_vars, $c_card)
@code_warntype test2(a_vals, b_vals, c_vars, c_card)
the last line shows the problem, its not type stable as you noted (hence why its slower), and its not type stable because the type that you create depends on the values of c_vars
and c_card
, since you’re explicitly sticking them into the type, so the compiler can’t know at compile type what type to return.
If you really want to stick those values into the type, the canonical way to do it type stabily is to use Val types, e.g. this has no overhead compared to your first test:
test3(a_vals, b_vals, ::Val{c_vars}, ::Val{c_card}) where {c_vars, c_card} = MyFactor{c_vars, c_card, Array{Float64, length(c_vars)}}(a_vals .* b_vals)
@btime test3($a_vals, $b_vals, $(Val(c_vars)), $(Val(c_card)))
but note that this means that this function and any function that takes a MyFactor
argument will be recompiled for every different possible value of c_vars
and c_card
you use. I would consider if you truly need those in the type, perhaps you do (although its not clear to me from your last example), but if you don’t, you’ll get faster compilation times and clearer code if you leave them out of the type.