Mysterious runtime difference

I would expect the two functions

f1(a; dims=:) = reduce(+, a; dims)
f2(a, b; dims=:) = b ? reduce(+, a; dims) : reduce(+, a; dims)

to have virtually identical runtime, but that is not the case:

julia> a=[1,2,3];
julia> @btime f1($a)
  5.615 ns (0 allocations: 0 bytes)
julia> @btime f2($a, true)
  328.360 ns (0 allocations: 0 bytes)

Tried in Julia 1.8.2, 1.9.0-beta4 and master. Is this a bug?

EDIT: One can make the difference even worse:

g1(a; dims=:) = invoke(reduce, Tuple{Any,AbstractArray}, +, a; dims)

function g2(a, b; dims=:)
    if b
        invoke(reduce, Tuple{Any,AbstractArray}, +, a; dims)
    else
        invoke(reduce, Tuple{Any,AbstractArray}, +, a; dims)    
    end
end
julia> @btime g1($a)
  5.298 ns (0 allocations: 0 bytes)
julia> @btime g2($a, true)
  1.520 μs (8 allocations: 496 bytes)

Appears to be related to f2 not specializing on its own default keyword argument (as a result of : subtyping Function, as mentioned in performance tips):

julia> f1(a; dims=:) = reduce(+, a; dims)
       f2(a, b; dims=:) = b ? reduce(+, a; dims) : reduce(+, a; dims)
       f3(a, b; dims::T=:) where T = b ? reduce(+, a; dims) : reduce(+, a; dims)
f3 (generic function with 1 method)

julia> a = [1,2,3]
       @btime f1($a)
       @btime f2($a, true)
       @btime f3($a, true)
  4.200 ns (0 allocations: 0 bytes)
  312.000 ns (0 allocations: 0 bytes)
  6.100 ns (0 allocations: 0 bytes)
6
1 Like

Great, thanks!

Is there good reason for : to subtype Function, I wonder? :thinking: