Is it possible to modify the following piece of code such that it becomes type-stable?
julia> function f(x)
for k in eachindex(x)
display(x[k])
end
end;
julia> a = tuple(1:2,1.0:2.0)
(1:2, 1.0:1.0:2.0)
julia> f(a)
1:2
1.0:1.0:2.0
julia> @code_warntype f(a)
Variables
#self#::Core.Compiler.Const(f, false)
x::Tuple{UnitRange{Int64},StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}
@_3::Union{Nothing, Tuple{Int64,Int64}}
k::Int64
Body::Nothing
1 ─ %1 = Main.eachindex(x)::Core.Compiler.Const(Base.OneTo(2), false)
│ (@_3 = Base.iterate(%1))
│ %3 = (@_3::Core.Compiler.Const((1, 1), false) === nothing)::Core.Compiler.Const(false, false)
│ %4 = Base.not_int(%3)::Core.Compiler.Const(true, false)
└── goto #4 if not %4
2 ┄ %6 = @_3::Tuple{Int64,Int64}::Tuple{Int64,Int64}
│ (k = Core.getfield(%6, 1))
│ %8 = Core.getfield(%6, 2)::Int64
│ %9 = Base.getindex(x, k)::Union{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}, U
nitRange{Int64}}
│ Main.display(%9)
│ (@_3 = Base.iterate(%1, %8))
│ %12 = (@_3 === nothing)::Bool
│ %13 = Base.not_int(%12)::Bool
└── goto #4 if not %13
3 ─ goto #2
4 ┄ return
Thank you very much in advance!
This code can not be made to be type stable. That said, it is unlikely to matter here.
3 Likes
display
is itself not stable and you can’t really fix that (nor is it a function that you might care about trying to), but for small tuples, you can get rid of the type instability introduced by the loop by using map
instead. E.g. compare the output of:
function f(x)
for k in eachindex(x)
identity(x[k])
end
end
@code_warntype f(a)
which will show the getindex
is not inferred (since the value of k
is unknown at compile time and the return type will depend on it):
│ %9 = Base.getindex(x, k)::Union{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}, UnitRange{Int64}}
vs. the output of:
function f(x)
map(identity, x)
end
@code_warntype f(a)
which is fully inferred. Note though even this will not be inferred if the tuple has more than 16 elements.
1 Like
Thank you very much for your quick responses! I should have chosen another MWE since I did not care about the display
part, only about the indexing into the tuple. Unfortunately, it is not possible to use map
for my purposes as I have to index into a product-iterator on the right-hand side…
However, I have fixed the type instability by promoting the types within the initial tuple, which, in spite of my concerns, did not significantly slow things down.
You can often use ntuple(i -> f(tup[i]), length(tup))
in place of a for
loop, which like map
behaves well for tuples.
3 Likes
My first approach to my problem had exactly this form and accordingly comprises only two lines of code. Unfortunately, it seems like there is no way to speed things up significantly… Filling the tuple manually with a for
-loop enables the use of multiple threads, which helps only in case of large inputs.
Thanks a lot for your reply!