I was writing a function that would ideally be written as
function ideal(supervec::Array{Array{Int64,1},1}, vec::Array{Int64,1})
return vcat(supervec[vec])
end
But this takes a bit more time than I think is unnecessary
julia> using BenchmarkTools
julia> @btime ideal([[1],[2]],[1,2])
232.497 ns (6 allocations: 576 bytes)
2-element Array{Array{Int64,1},1}:
[1]
[2]
So, I tried to write a longer version
function simplecount(supervec::Array{Array{Int64,1},1}, vec::Array{Int64,1})
consize = sum(a->length(supervec[a]),vec)
con = Array{Int64,1}(undef,consize)
counter = 0
for j = 1:length(vec)
b = vec[j]
altvec = supervec[b]
for r = 1:length(supervec[b])
p = altvec[r]
counter += 1
con[counter] = p
end
end
return con
end
This performs better
julia> @btime simplecount([[1],[2]],[1,2])
177.572 ns (5 allocations: 480 bytes)
2-element Array{Int64,1}:
1
2
But it has a type instability when I use @code_warntype
julia> @code_warntype simplecount([[1],[2]],[1,2])
Variables
#self#::Core.Compiler.Const(simplecount, false)
supervec::Array{Array{Int64,1},1}
vec::Array{Int64,1}
#97::var"#97#98"{Array{Array{Int64,1},1}}
val::Nothing
consize::Int64
con::Array{Int64,1}
counter::Int64
@_9::Union{Nothing, Tuple{Int64,Int64}}
j::Int64
b::Int64
altvec::Array{Int64,1}
@_13::Union{Nothing, Tuple{Int64,Int64}}
r::Int64
p::Int64
Body::Array{Int64,1}
1 ─ Core.NewvarNode(:(val))
│ %2 = Main.:(var"#97#98")::Core.Compiler.Const(var"#97#98", false)
│ %3 = Core.typeof(supervec)::Core.Compiler.Const(Array{Array{Int64,1},1}, false)
│ %4 = Core.apply_type(%2, %3)::Core.Compiler.Const(var"#97#98"{Array{Array{Int64,1},1}}, false)
│ (#97 = %new(%4, supervec))
│ %6 = #97::var"#97#98"{Array{Array{Int64,1},1}}
│ (consize = Main.sum(%6, vec))
│ %8 = Core.apply_type(Main.Array, Main.intType, 1)::Core.Compiler.Const(Array{Int64,1}, false)
│ (con = (%8)(Main.undef, consize))
│ (counter = 0)
│ $(Expr(:inbounds, true))
│ %12 = Main.length(vec)::Int64
│ %13 = (1:%12)::Core.Compiler.PartialStruct(UnitRange{Int64}, Any[Core.Compiler.Const(1, false), Int64])
│ (@_9 = Base.iterate(%13))
│ %15 = (@_9 === nothing)::Bool
│ %16 = Base.not_int(%15)::Bool
└── goto #7 if not %16
2 ┄ %18 = @_9::Tuple{Int64,Int64}::Tuple{Int64,Int64}
│ (j = Core.getfield(%18, 1))
│ %20 = Core.getfield(%18, 2)::Int64
│ (b = Base.getindex(vec, j))
│ (altvec = Base.getindex(supervec, b))
│ %23 = Base.getindex(supervec, b)::Array{Int64,1}
│ %24 = Main.length(%23)::Int64
│ %25 = (1:%24)::Core.Compiler.PartialStruct(UnitRange{Int64}, Any[Core.Compiler.Const(1, false), Int64])
│ (@_13 = Base.iterate(%25))
│ %27 = (@_13 === nothing)::Bool
│ %28 = Base.not_int(%27)::Bool
└── goto #5 if not %28
3 ┄ %30 = @_13::Tuple{Int64,Int64}::Tuple{Int64,Int64}
│ (r = Core.getfield(%30, 1))
│ %32 = Core.getfield(%30, 2)::Int64
│ (p = Base.getindex(altvec, r))
│ (counter = counter + 1)
│ Base.setindex!(con, p, counter)
│ (@_13 = Base.iterate(%25, %32))
│ %37 = (@_13 === nothing)::Bool
│ %38 = Base.not_int(%37)::Bool
└── goto #5 if not %38
4 ─ goto #3
5 ┄ (@_9 = Base.iterate(%13, %20))
│ %42 = (@_9 === nothing)::Bool
│ %43 = Base.not_int(%42)::Bool
└── goto #7 if not %43
6 ─ goto #2
7 ┄ (val = nothing)
│ $(Expr(:inbounds, :pop))
│ val
└── return con
Why is the iterator in the innermost for-loop unable to distinguish between Nothing
and Tuple
(the line @_9::Union{Nothing, Tuple{Int64,Int64}}
is yellow)? Is there a way to force the compiler to know which one it should be? Is there is a simpler way to write something like this? Thanks a lot!