Short version: is a pattern like issymmetric(x) && (x = Symmetric(x))
type-unstable, and will it have performance consequences? Details below.
I have a user-facing function that looks something like
function f(x::Matrix)
for i in 1:big_number
helper(x)
end
end
And helper
has a fast version and a slow version:
helper(x::AbstractMatrix) = ... # slow
helper(x::Symmetric) = ... # fast
I’d like to efficiently use the version of helper
appropriate to whether or not x
is symmetric, without requiring the user to themselves wrap x
in a Symmetric
view. That is, I don’t want to have two methods f(x::Symmetric)
and f(x::AbstractMatrix)
.
So I have two ideas:
1. Use a function barrier:
function f(x)
issymmetric(x) && (x = Symmetric(x))
return _f_logic(x)
end
function _f_logic(x)
for i in 1:big_number
helper(x)
end
end
2. Simply wrap x
in Symmetric
within the main function:
function f(x::Matrix)
issymmetric(x) && (x = Symmetric(x))
for i in 1:big_number
helper(x)
end
end
Now, I’d expect - and @code_warntype
seems to confirm - that option 2 introduces type instability since the compiler can’t know the type of x
within the big loop; that type depends on the issymmetric
call which depends on runtime values. In practice, however, there’s no meaningful performance difference between these two approaches and option 2 seems rather more readable to me.
Am I missing something? Is there a more idiomatic way to deal with this use case?