`TypeVar` or `eltype`, which is faster?

Consider the following code

using Unitful

function f(arr::AbstractArray{T}) where {T}
    N = length(arr)
    return NTuple{N,T}(arr)
end

or

function g(arr::AbstractArray)
    N, T = length(arr), eltype(arr)
    return NTuple{N,T}(arr)
end

The first one has $(Expr(:static_parameter, 1)) in its @code_warntype

julia> @code_warntype f([1u"m", 2u"GPa", 3.0, 4])
Variables
  #self#::Core.Compiler.Const(f, false)
  arr::Array{Quantity{Float64,D,U} where U where D,1}
  N::Int64

Body::Tuple
1 ─      (N = Main.length(arr))
│   %2 = Core.apply_type(Main.NTuple, N, $(Expr(:static_parameter, 1)))::Type{Tuple{Vararg{Quantity{Float64,D,U} where U where D,_A}}} where _A
│   %3 = (%2)(arr)::Tuple
└──      return %3

while the second has Core.Compiler.Const

julia> @code_warntype g([1u"m", 2u"GPa", 3.0, 4])
Variables
  #self#::Core.Compiler.Const(g, false)
  arr::Array{Quantity{Float64,D,U} where U where D,1}
  N::Int64
  T::Type{Quantity{Float64,D,U} where U where D}

Body::Tuple
1 ─ %1 = Main.length(arr)::Int64
│   %2 = Main.eltype(arr)::Core.Compiler.Const(Quantity{Float64,D,U} where U where D, false)
│        (N = %1)
│        (T = %2)
│   %5 = Core.apply_type(Main.NTuple, N, T::Core.Compiler.Const(Quantity{Float64,D,U} where U where D, false))::Type{Tuple{Vararg{Quantity{Float64,D,U} where U where D,_A}}} where _A
│   %6 = (%5)(arr)::Tuple
└──      return %6

Which one should be faster? What’s the difference? Which is more idiomatic?

julia> @btime f([1u"m", 2u"GPa", 3.0, 4])
  4.279 μs (48 allocations: 1.47 KiB)
(1.0 m, 2.0 GPa, 3.0, 4.0)

julia> @btime g([1u"m", 2u"GPa", 3.0, 4])
  4.296 μs (48 allocations: 1.47 KiB)
(1.0 m, 2.0 GPa, 3.0, 4.0)

julia> @btime f([1u"m", 2u"GPa", 3.0, 4])
  4.283 μs (48 allocations: 1.47 KiB)
(1.0 m, 2.0 GPa, 3.0, 4.0)

julia> @btime g([1u"m", 2u"GPa", 3.0, 4])
  4.303 μs (48 allocations: 1.47 KiB)
(1.0 m, 2.0 GPa, 3.0, 4.0)

julia> @btime $f([1u"m", 2u"GPa", 3.0, 4])
  4.312 μs (48 allocations: 1.47 KiB)
(1.0 m, 2.0 GPa, 3.0, 4.0)

julia> @btime $g([1u"m", 2u"GPa", 3.0, 4])
  4.302 μs (48 allocations: 1.47 KiB)
(1.0 m, 2.0 GPa, 3.0, 4.0)

julia> @btime $f([1u"m", 2u"GPa", 3.0, 4])
  4.306 μs (48 allocations: 1.47 KiB)
(1.0 m, 2.0 GPa, 3.0, 4.0)

julia> @btime $g([1u"m", 2u"GPa", 3.0, 4])
  4.273 μs (48 allocations: 1.47 KiB)
(1.0 m, 2.0 GPa, 3.0, 4.0

They should always compile to essentially the same thing. That said, I feel like the first is slightly more clear.

1 Like