Hey Julianners,
I just cannot figure out how to make type stable code in case when we have a “sort of” heterogeneous array or tuple and we randomly pick an element.
I did this minimal example.
hn(qq) = begin
local r
q=qq[rand([1,3])]
if isa(q, Int)
r=4
elseif isa(q,Float32)
r=3f0
else
r=1e0
end
return r
end
@code_warntype hn((3,3f0,2))
hn((3,3f0,2))
Is this something that isn’t possible to resolve in any certain way in Julia?
Your function is fundamentally unstable, it literally draws a random type at runtime. Your type assertions only make individual branches type stable, but the return value of the function is still unknown to the compiler.
2 Likes
Yeah, you are right… I just want this to be type stable…
I guess you basically proved it isn’t possible.
So:
fn(arr::heterogenous type) =
x=arr[pick random] # only available in run time
return x # cannot be deferred or fixed any certain way?
end
But then how to handle the heterogeneous types in Julia?
In C++ we could write a very performant code with virtual functions I think. Does this something that cannot be accomplised with Julia?
You can use the trick with type assertions like you did, but inside the branches you should call a function that performs the work. You want to avoid the instability propagating to the compute heavy parts of your code.
1 Like
When you have heterogeneous data whose elements are accessed based on runtime information, try to minimize the code that runs in the uninferred state, i.e. wrap subsequent code in a function that can be specialized to the different types. Using the profiler, you can then check that often the run-time dispatch overhead is negligible. That is, the fact that the top function call is type-unstable is not actually detrimental to performance.
1 Like
So basically I could write something like this and it is optimal:
calc(a::Vector{Float32}) = a.+=1f0
calc(a::Vector{Float64}) = a.+=1e0
qn(hetarrs) = begin
arr = hetarrs[rand([1,2])]
calc(arr)
end
vv = (
randn(Float32,10000),
randn(Float64,10000),
)
# @code_warntype qn(vv)
qn(vv)
Yes, the advantage of doing it this way is that you pay the cost of the type instability a single time, at the call for calc
.
1 Like
This approach is called a “function barrier” where qn
is unstable, but the calc
function serves as a barrier for the instability beyond which the types are known. The call to calc
incurs dynamic dispatch, but calc
itself is not affected.
2 Likes