I have a function f
that is potentially type-unstable. I also have a function g
that in the end is type-stable, but inside it calls f
.
Here is an (artificial) example, implemented in different ways:
f1(t::Tuple, i::Int) = t[i]
g1(thetupl::Tuple, ::Val{n}) where {n} = ntuple(i -> f1(thetupl, i), Val(n))
f2(t::Tuple, ::Val{i}) where {i} = t[i]
g2(thetupl::Tuple, ::Val{n}) where {n} = ntuple(i -> f2(thetupl, Val(i)), Val(n))
f3(t::Tuple, ::Val{i}) where {i} = t[i]
f3(t::Tuple, i::Int) = t[i]
g3(thetupl::Tuple, ::Val{n}) where {n} = ntuple(i -> f3(thetupl, i), Val(n))
g4(thetupl::Tuple, ::Val{n}) where {n} = ntuple(i -> f3(thetupl, Val(i)), Val(n))
f5(t::Tuple, i::Int) = f2(t::Tuple, Val(i))
g5(thetupl::Tuple, ::Val{n}) where {n} = ntuple(i -> f5(thetupl, i), Val(n))
All the versions of g
are type-stable (in theory). However f1
, f3
, f5
are potentially type-unstable because their output types can depend on the value of i
, when the tuple t
has items of heterogeneous types.
To experiment, I created an heterogeneous tuple:
julia> thetupl = ntuple(i -> rand((rand(1:10), rand("da;glaergerf"), "afdsa", rand(Bool), randn())), 20)
("afdsa", false, false, "afdsa", "afdsa", true, 9, false, 4, false, 'l', 'l', 'e', false, 0.21198045684894534, -0.9550998919808197, 'f', false, "afdsa", "afdsa")
Then I inspected the type-stability of the different versions of g
as inferred by Julia, using @inferred g1(thetupl, Val(5))
, and so on for the other variations of g
. Turns out that g1
, g3
are seen as type-stable by Julia, while g2
, g4
, g5
are not! I was surprised by this, and would like to understand why.
I actually have some slightly more complicated code with a related issue, so I am trying to understand what is the best way to deal with these “spurious” type instabilities inside type-stable functions.
In case this depends on compiler internals that might change with version, I am using Julia 1.3.1 here.