Why would `<:` in Unions for keyword arguments create allocations

Take this MWE function which simply returns the argument at the specified index idx or y in the vector v:

function foo(v::Vector{Int}, idx::Union{Nothing,Int}=nothing; y::Union{Nothing,Integer}=nothing)
  if !xor(isnothing(idx), isnothing(y))
  error("invalid arguments")
  end

  if isnothing(y)
    return v[idx]
  else
    return v[y]
  end
end

With Benchmark tools we see:

julia> a = [1,2,3,4,5,6,7,8,9,10];

julia> @btime foo($a,1);
  1.375 ns (0 allocations: 0 bytes)

julia> @btime foo($a,y=1);
  1.333 ns (0 allocations: 0 bytes)

No surprises here, zero allocations. Now let’s make one tiny change and let the keyword argument type be Union{Nothing, <:Integer} instead of Union{Nothing, Integer}, so:

function foo2(v::Vector{Int}, idx::Union{Nothing,Int}=nothing; y::Union{Nothing,<:Integer}=nothing)
  if !xor(isnothing(idx), isnothing(y))
  error("invalid arguments")
  end

  if isnothing(y)
    return v[idx]
  else
    return v[y]
  end
end

Now we see with foo2:

julia> @btime foo2($a,1);
  1.333 ns (0 allocations: 0 bytes)

julia> @btime foo2($a,y=1);
  184.793 ns (3 allocations: 96 bytes)

Why would there be allocations by having the <: in vs not for the keyword argument? I do not see similar behavior when changing the non-keyword argument to <:Integer?

1 Like

I think that’s a bug, note that with Union{Nothing, T} where {T<:Integer} the function does not allocate, and AFAIU, it should be the same. Here a reduced example:

julia> foo(v; y::Union{Nothing,<:Integer}=nothing) = isnothing(y) ? v[1] : v[y]
foo (generic function with 1 method)

julia> @btime foo($[1,2,3];y=1);
  286.996 ns (3 allocations: 96 bytes)

julia> foo(v; y::Union{Nothing,Integer}=nothing) = isnothing(y) ? v[1] : v[y]
foo (generic function with 1 method)

julia> @btime foo($[1,2,3];y=1);
  2.609 ns (0 allocations: 0 bytes)

julia> foo(v; y::Union{Nothing,T}=nothing) where {T<:Integer} = isnothing(y) ? v[1] : v[y]
foo (generic function with 1 method)

julia> @btime foo($[1,2,3];y=1);
  3.002 ns (0 allocations: 0 bytes)
2 Likes

Reported

1 Like