Are you sure that test(...) allocates?
julia> const tup_const = (10, 100, 1., 2., 3.)
(10, 100, 1.0, 2.0, 3.0)
julia> function test(tup, idx, type::Type{T}) where T
ret::T = tup[idx]
ret
end
test (generic function with 1 method)
julia> @time test(tup_const, 3, Float64)
0.000003 seconds
1.0
julia> @btime test(tup_const, 3, Float64)
1.041 ns (0 allocations: 0 bytes)
1.0
You may also want to consider the effect of constant propagation.
Note the union type below.
julia> @code_warntype test(tup_const, 3, Float64)
MethodInstance for test(::Tuple{Int64, Int64, Float64, Float64, Float64}, ::Int64, ::Type{Float64})
from test(tup, idx, type::Type{T}) where T @ Main REPL[27]:1
Static Parameters
T = Float64
Arguments
#self#::Core.Const(test)
tup::Tuple{Int64, Int64, Float64, Float64, Float64}
idx::Int64
type::Core.Const(Float64)
Locals
ret::Float64
@_6::Union{Float64, Int64}
Body::Float64
1 ─ Core.NewvarNode(:(ret))
│ %2 = Base.getindex(tup, idx)::Union{Float64, Int64}
│ (@_6 = %2)
│ %4 = (@_6 isa $(Expr(:static_parameter, 1)))::Bool
└── goto #3 if not %4
2 ─ goto #4
3 ─ %7 = Base.convert($(Expr(:static_parameter, 1)), @_6::Int64)::Float64
└── (@_6 = Core.typeassert(%7, $(Expr(:static_parameter, 1))))
4 ┄ (ret = @_6::Float64)
└── return ret
If we create a function with the index three hard-coded in, we see the absence of instability.
julia> function test2(tup, idx)
tup[idx]
end
test2 (generic function with 2 methods)
julia> f(tup) = @inline test2(tup, 3)
f (generic function with 1 method)
julia> @code_warntype f(tup_const)
MethodInstance for f(::Tuple{Int64, Int64, Float64, Float64, Float64})
from f(tup) @ Main REPL[43]:1
Arguments
#self#::Core.Const(f)
tup::Tuple{Int64, Int64, Float64, Float64, Float64}
Locals
val::Float64
Body::Float64
1 ─ nothing
│ (val = Main.test2(tup, 3))
│ nothing
└── return val