Skip to content

Inference issue with collect() on generators with filter #25433

Closed
@nalimilan

Description

I've noticed this when changing find to use generators at #24774. It's likely to have a large performance impact.

julia> @code_warntype collect(v for v in [1] if v > 0)
[...]
  end::Union{Array{Int64,1}, Array{Union{},1}}

This type instability also affects grow_to!. I first thought it could be due to the type-instability of start(::Filter), here #temp#@_9::Union{Tuple{Bool}, Tuple{Bool,Int64,Int64}}, but artificially fixing it by making Base.Iterators.start_filter return (true, 1, 2) as the fallback does not make the problem disappear.

julia> @code_warntype Base.grow_to!(Vector{Int}(), (v for v in [1] if v > 0))
Variables:
  dest::Array{Int64,1}
  itr::Base.Generator{Base.Iterators.Filter{getfield(, Symbol("##104#106")),Array{Int64,1}},getfield(, Symbol("##103#105"))}
  out::Any
  s::Int64
  v<optimized out>
  t<optimized out>
  #temp#@_9::Union{Tuple{Bool}, Tuple{Bool,Int64,Int64}}
  #temp#@_10::Any
  #temp#@_11::Bool
[...]
      #= line 397 =#
      #temp#@_9::Union{Tuple{Bool}, Tuple{Bool,Int64,Int64}} = (true,)
      59:
      # meta: pop location
      SSAValue(17) = #temp#@_9::Union{Tuple{Bool}, Tuple{Bool,Int64,Int64}}
      # meta: pop locations (2)
      SSAValue(50) = (SSAValue(17) isa Tuple{Bool})::Bool
      unless SSAValue(50) goto 67
      #temp#@_10::Any = $(Expr(:invoke, MethodInstance for grow_to!(::Array{Union{},1}, ::Base.Generator{Base.Iterators.Filter{getfield(, Symbol("##104#106")),Array{Int64,1}},getfield(, Symbol("##103#105"))}, ::Tuple{Bool}), :(Base.grow_to!), SSAValue(11), :(itr), SSAValue(17)))::Union{Array{Int64,1}, Array{Union{},1}}
      goto 76
      67:
      SSAValue(51) = (SSAValue(17) isa Tuple{Bool,Int64,Int64})::Bool
      unless SSAValue(51) goto 72
      #temp#@_10::Any = $(Expr(:invoke, MethodInstance for grow_to!(::Array{Union{},1}, ::Base.Generator{Base.Iterators.Filter{getfield(, Symbol("##104#106")),Array{Int64,1}},getfield(, Symbol("##103#105"))}, ::Tuple{Bool,Int64,Int64}), :(Base.grow_to!), SSAValue(11), :(itr), SSAValue(17)))::Union{Array{Int64,1}, Array{Union{},1}}
[...]
      return dest::Array{Int64,1}
      96:
      return out::Any
  end::Union{Array{Int64,1}, Array{Union{},1}}

julia> @code_warntype Base.grow_to!(Vector{Int}(), (v for v in [1] if v > 0), (false, 1, 2))
[...]
  end::Array{Int64,1}

julia> @code_warntype Base.grow_to!(Vector{Int}(), (v for v in [1] if v > 0), (false,))
[...]
  end::Array{Int64,1}

So the explanation might be that the function could theoretically return new rather than dest, and that new could have its type change in the loop. This might confuse inference when grow_to!(dest, itr) is inlined in grow_to!(dest, itr). I've tried using @noinline, but it doesn't appear to have an effect here.

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions