Hi,
I am looking for a way to make this kind of operation type-stable. There are many uses to structs with lists of callables but I canβt seem to find a way to make the concatenation of their outputs type-stable
julia> struct Foo{T}
fs::T
end
julia> (foo::Foo)(tup) = vcat([foo.fs[1](tup[1]) for i in 1:lenght(tup)]...)
julia> foo = Foo((x->diff(x), x-> round.(x)))
Foo{Tuple{var"#15#17",var"#16#18"}}((var"#15#17"(), var"#16#18"()))
julia> @code_warntype foo((rand(5), rand(5)))
Variables
foo::Core.Compiler.Const(Foo{Tuple{var"#15#17",var"#16#18"}}((var"#15#17"(), var"#16#18"())), false)
tup::Tuple{Array{Float64,1},Array{Float64,1}}
#9::var"#9#10"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}
Body::Any
1 β %1 = Main.:(var"#9#10")::Core.Compiler.Const(var"#9#10", false)
β %2 = Core.typeof(foo)::Core.Compiler.Const(Foo{Tuple{var"#15#17",var"#16#18"}}, false)
β %3 = Core.typeof(tup)::Core.Compiler.Const(Tuple{Array{Float64,1},Array{Float64,1}}, false)
β %4 = Core.apply_type(%1, %2, %3)::Core.Compiler.Const(var"#9#10"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}, false)
β (#9 = %new(%4, foo, tup))
β %6 = #9::var"#9#10"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}
β %7 = Main.lenght(tup)::Any
β %8 = (1:%7)::Any
β %9 = Base.Generator(%6, %8)::Base.Generator{_A,var"#9#10"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}} where _A
β %10 = Base.collect(%9)::Array{T,N} where T where N
β %11 = Core._apply_iterate(Base.iterate, Main.vcat, %10)::Any
βββ return %11
I also tried with reduce
:
julia> (foo::Foo)(tup) = reduce(vcat, [foo.fs[1](tup[1]) for i in 1:lenght(tup)])
julia> @code_warntype foo((rand(5), rand(5)))
Variables
foo::Core.Compiler.Const(Foo{Tuple{var"#15#17",var"#16#18"}}((var"#15#17"(), var"#16#18"())), false)
tup::Tuple{Array{Float64,1},Array{Float64,1}}
#19::var"#19#20"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}
Body::Any
1 β %1 = Main.:(var"#19#20")::Core.Compiler.Const(var"#19#20", false)
β %2 = Core.typeof(foo)::Core.Compiler.Const(Foo{Tuple{var"#15#17",var"#16#18"}}, false)
β %3 = Core.typeof(tup)::Core.Compiler.Const(Tuple{Array{Float64,1},Array{Float64,1}}, false)
β %4 = Core.apply_type(%1, %2, %3)::Core.Compiler.Const(var"#19#20"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}, false)
β (#19 = %new(%4, foo, tup))
β %6 = #19::var"#19#20"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}
β %7 = Main.lenght(tup)::Any
β %8 = (1:%7)::Any
β %9 = Base.Generator(%6, %8)::Base.Generator{_A,var"#19#20"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}} where _A
β %10 = Base.collect(%9)::Array{T,N} where T where N
β %11 = Main.reduce(Main.vcat, %10)::Any
βββ return %11
For some reason, the compiler doesnβt seem to catch that lenght(tup)
is an Int. If I replace by a constant (which is not desirable) I get a type-stable operation but the vcat still is type-stable:
julia> (foo::Foo)(tup) = vcat([foo.fs[1](tup[1]) for i in 1:2]...)
julia> @code_warntype foo((rand(5), rand(5)))
Variables
foo::Core.Compiler.Const(Foo{Tuple{var"#15#17",var"#16#18"}}((var"#15#17"(), var"#16#18"())), false)
tup::Tuple{Array{Float64,1},Array{Float64,1}}
#25::var"#25#26"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}
Body::Union{Array{Any,1}, Array{Float64,1}}
1 β %1 = Main.:(var"#25#26")::Core.Compiler.Const(var"#25#26", false)
β %2 = Core.typeof(foo)::Core.Compiler.Const(Foo{Tuple{var"#15#17",var"#16#18"}}, false)
β %3 = Core.typeof(tup)::Core.Compiler.Const(Tuple{Array{Float64,1},Array{Float64,1}}, false)
β %4 = Core.apply_type(%1, %2, %3)::Core.Compiler.Const(var"#25#26"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}, false)
β (#25 = %new(%4, foo, tup))
β %6 = #25::var"#25#26"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}
β %7 = (1:2)::Core.Compiler.Const(1:2, false)
β %8 = Base.Generator(%6, %7)::Core.Compiler.PartialStruct(Base.Generator{UnitRange{Int64},var"#25#26"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}}, Any[var"#25#26"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}, Core.Compiler.Const(1:2, false)])
β %9 = Base.collect(%8)::Array{Array{Float64,1},1}
β %10 = Core._apply_iterate(Base.iterate, Main.vcat, %9)::Union{Array{Any,1}, Array{Float64,1}}
βββ return %10
With reduce, I get a type-stable function but reduce applies vcat
several times which seems unefficient:
julia> (foo::Foo)(tup) = reduce(vcat, [foo.fs[1](tup[1]) for i in 1:2])
julia> @code_warntype foo((rand(5), rand(5)))
Variables
foo::Core.Compiler.Const(Foo{Tuple{var"#15#17",var"#16#18"}}((var"#15#17"(), var"#16#18"())), false)
tup::Tuple{Array{Float64,1},Array{Float64,1}}
#23::var"#23#24"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}
Body::Array{Float64,1}
1 β %1 = Main.:(var"#23#24")::Core.Compiler.Const(var"#23#24", false)
β %2 = Core.typeof(foo)::Core.Compiler.Const(Foo{Tuple{var"#15#17",var"#16#18"}}, false)
β %3 = Core.typeof(tup)::Core.Compiler.Const(Tuple{Array{Float64,1},Array{Float64,1}}, false)
β %4 = Core.apply_type(%1, %2, %3)::Core.Compiler.Const(var"#23#24"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}, false)
β (#23 = %new(%4, foo, tup))
β %6 = #23::var"#23#24"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}
β %7 = (1:2)::Core.Compiler.Const(1:2, false)
β %8 = Base.Generator(%6, %7)::Core.Compiler.PartialStruct(Base.Generator{UnitRange{Int64},var"#23#24"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}}, Any[var"#23#24"{Foo{Tuple{var"#15#17",var"#16#18"}},Tuple{Array{Float64,1},Array{Float64,1}}}, Core.Compiler.Const(1:2, false)])
β %9 = Base.collect(%8)::Array{Array{Float64,1},1}
β %10 = Main.reduce(Main.vcat, %9)::Array{Float64,1}
βββ return %10
So, I see two problems here. 1), vcat(::Array{Array}...)
is not type-stable (::Union{Array{Any,1}, Array{Float64,1}}
). 2) length(tup)
is ::Any
.
How would you do this efficiently ?