I want to do elementwise operations on Array{Union{A,B,C}, M}
, and I want an output of Array{Union{A,B,C}, N}
without dynamic dispatch caused by failure to infer the Union{A,B,C}
element type.
I pasted the examples I'm playing with below. I am aware I could use type annotations to control return types e.g. local y::eltype(x)
, but it doesn't stop Any
inferences in the elementwise operations (as shown by @code_warntype) or dynamic dispatch (won't affect allocation numbers, which I report later).
It seems like Union splitting works up to 3 concrete types in the example, but while looping splits the element type, broadcasting splits the Vector
type instead of the element type parameter. I'm asking for generalizable approaches to writing loops or elementwise methods that get around these inference limitations, even if it gets as tedious as conditional statements for each type.
begin
function loop(x)
local y
for i in x
y = i
end
return y
end
function each1(x)
return x+1
end
# inputs to test
const x3 = Union{Int, Float64, Bool}[1, 1.1, true]
const x4 = Union{Int, Float64, Bool, ComplexF64}[1, 1.1, true, 1.1im]
end
I won't paste the whole @code_warntype
printouts, just the inferred output types. Also I'm not exactly sure how to measure dynamic dispatch, but it causes extra allocations so I'm reporting the allocations in the 2nd runs of @time
.
typeof(loop(x3)) # Bool
@code_warntype loop(x3) # Union{Bool, Float64, Int64}
@time loop(x3) # 0 allocations
typeof(each1.(x3)) # Vector{Real}
@code_warntype each1.(x3) # Union{Vector{Float64}, Vector{Int64}, Vector{Real}}
@time each1.(x3) # 3 allocations
typeof(loop(x4)) # ComplexF64
@code_warntype loop(x4) # Any
@time loop(x4) # 8 allocations
typeof(each1.(x4)) # Vector{Number}
@code_warntype each1.(x4) # AbstractVector{<:Number}
@time each1.(x4) # 13 allocations