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?