Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inference discards bounds on abstract parameters #36454

Closed
timholy opened this issue Jun 27, 2020 · 6 comments
Closed

Inference discards bounds on abstract parameters #36454

timholy opened this issue Jun 27, 2020 · 6 comments
Labels
compiler:inference Type inference

Comments

@timholy
Copy link
Sponsor Member

timholy commented Jun 27, 2020

Apologies, I'm sure this must have been reported before but a search didn't pick it up.

There appear to be cases where it would be "easy" to preserve bounds on abstract types. One that comes up a lot in my invalidation-squashing is Iterators.Stateful:

julia> code_typed(Iterators.Stateful, (AbstractString,))
1-element Array{Any,1}:
 CodeInfo(
1%1 = invoke Base.Iterators.approx_iter_type($(Expr(:static_parameter, 1))::Type{T} where T)::Type%2 = Core.apply_type(Base.Iterators.Stateful, $(Expr(:static_parameter, 1)), %1)::Type{Base.Iterators.Stateful{_A,_B}} where _B where _A
│   %3 = Base.convert($(Expr(:static_parameter, 1)), itr)::Any%4 = Core.fieldtype(%2, 2)::Type{var"#s428"} where var"#s428"<:(Union{Nothing, _B} where _B)%5 = Base.Iterators.iterate(itr)::Any
│        Core.typeassert(%5, %1)::Any%7 = Base.convert(%4, %5)::Any%8 = %new(%2, %3, %7, 0)::Base.Iterators.Stateful{_A,_B} where _B where _A
└──      return %8
) => Base.Iterators.Stateful{_A,_B} where _B where _A

Given the definition

julia/base/iterators.jl

Lines 1243 to 1246 in d762e8c

@inline function Stateful(itr::T) where {T}
VS = approx_iter_type(T)
return new{T, VS}(itr, iterate(itr)::VS, 0)
end
it would naively seem fairly straightforward to retain _A<:AbstractString.

@timholy timholy added the compiler:inference Type inference label Jun 27, 2020
@martinholters
Copy link
Member

The code to fix this is all there it seems, just disabled:

julia/base/compiler/tfuncs.jl

Lines 1100 to 1123 in 6185d24

# These blocks improve type info but make compilation a bit slower.
# XXX
#unw = unwrap_unionall(ai)
#isT = isType(unw)
#if isT && isa(ai,UnionAll) && contains_is(outervars, ai.var)
# ai = rename_unionall(ai)
# unw = unwrap_unionall(ai)
#end
if istuple
if i == largs
push!(tparams, Vararg)
# XXX
#elseif isT
# push!(tparams, rewrap_unionall(unw.parameters[1], ai))
else
push!(tparams, Any)
end
# XXX
#elseif isT
# push!(tparams, unw.parameters[1])
# while isa(ai, UnionAll)
# push!(outervars, ai.var)
# ai = ai.body
# end

If this came up in your invalidation hunt, maybe it's time to reconsider this?

@timholy
Copy link
Sponsor Member Author

timholy commented Jun 29, 2020

Nice find. It's a difficult design decision; on one hand, doing more accurate inference makes inference slower. On the other hand, doing more accurate inference reduces the "vulnerability" of code to invalidation because fewer MethodInstances end up having mt_backedges to Any-instances of their dependent methods. Thereby, investing in better inference at the outset might save inference in the long run.

It's quite difficult to come up with reasonable benchmarks for these things, since it depends entirely on what new methods you define. Therefore what works in practice is a bit of a cultural issue of which packages often get used in which combinations.

timholy added a commit to JuliaLang/Pkg.jl that referenced this issue Jun 30, 2020
JuliaLang/julia#36280 introduced the ability to
pre-allocate the container used to track values of `f.(itr)` in
`unique(f, itr)`. Particularly for containers with `Union` elements,
this circumvents significant inference problems.

Related: JuliaLang/julia#36454
timholy added a commit to JuliaLang/Pkg.jl that referenced this issue Jun 30, 2020
JuliaLang/julia#36280 introduced the ability to
pre-allocate the container used to track values of `f.(itr)` in
`unique(f, itr)`. Particularly for containers with `Union` elements,
this circumvents significant inference problems.

Related: JuliaLang/julia#36454
timholy added a commit that referenced this issue Jan 17, 2021
`cat` is often called with Varargs or heterogenous inputs,
and inference almost always fails. Even when all the arrays
are of the same type, if the number of varargs isn't known
inference typically fails. The culprit is probably #36454.

This reduces the number of failures considerably, by avoiding
creation of vararg length tuples in the shape-inference pipeline.
timholy added a commit that referenced this issue Jan 19, 2021
`cat` is often called with Varargs or heterogenous inputs,
and inference almost always fails. Even when all the arrays
are of the same type, if the number of varargs isn't known
inference typically fails. The culprit is probably #36454.

This reduces the number of failures considerably, by avoiding
creation of vararg length tuples in the shape-inference pipeline.
KristofferC pushed a commit that referenced this issue Jan 20, 2021
`cat` is often called with Varargs or heterogenous inputs,
and inference almost always fails. Even when all the arrays
are of the same type, if the number of varargs isn't known
inference typically fails. The culprit is probably #36454.

This reduces the number of failures considerably, by avoiding
creation of vararg length tuples in the shape-inference pipeline.

(cherry picked from commit 815076b)
KristofferC pushed a commit that referenced this issue Feb 1, 2021
`cat` is often called with Varargs or heterogenous inputs,
and inference almost always fails. Even when all the arrays
are of the same type, if the number of varargs isn't known
inference typically fails. The culprit is probably #36454.

This reduces the number of failures considerably, by avoiding
creation of vararg length tuples in the shape-inference pipeline.

(cherry picked from commit 815076b)
timholy added a commit to timholy/TiffImages.jl that referenced this issue Feb 25, 2021
Inference loses track of `Tag` due to
JuliaLang/julia#36454
ElOceanografo pushed a commit to ElOceanografo/julia that referenced this issue May 4, 2021
`cat` is often called with Varargs or heterogenous inputs,
and inference almost always fails. Even when all the arrays
are of the same type, if the number of varargs isn't known
inference typically fails. The culprit is probably JuliaLang#36454.

This reduces the number of failures considerably, by avoiding
creation of vararg length tuples in the shape-inference pipeline.
antoine-levitt pushed a commit to antoine-levitt/julia that referenced this issue May 9, 2021
`cat` is often called with Varargs or heterogenous inputs,
and inference almost always fails. Even when all the arrays
are of the same type, if the number of varargs isn't known
inference typically fails. The culprit is probably JuliaLang#36454.

This reduces the number of failures considerably, by avoiding
creation of vararg length tuples in the shape-inference pipeline.
staticfloat pushed a commit that referenced this issue Dec 23, 2022
`cat` is often called with Varargs or heterogenous inputs,
and inference almost always fails. Even when all the arrays
are of the same type, if the number of varargs isn't known
inference typically fails. The culprit is probably #36454.

This reduces the number of failures considerably, by avoiding
creation of vararg length tuples in the shape-inference pipeline.

(cherry picked from commit 815076b)
@ChrisRackauckas
Copy link
Member

Bump this up to triage to reconsider given the many changes to precompilation and the increased cost of invalidations?

@timholy
Copy link
Sponsor Member Author

timholy commented Dec 28, 2022

I think that's a good idea, but probably first someone should collect some data to bring to the discussion.

@gbaraldi
Copy link
Member

gbaraldi commented Jan 5, 2023

Triage thinks that, as Tim said, this probably needs more data for a more informed decision.

@gbaraldi gbaraldi removed the status:triage This should be discussed on a triage call label Jan 5, 2023
@vtjnash
Copy link
Sponsor Member

vtjnash commented Aug 7, 2024

code is enabled now

julia> code_typed(Iterators.Stateful, (AbstractString,))

1-element Vector{Any}:
 CodeInfo(
    @ iterators.jl:1452 within `Stateful`
1 ─ %1  = $(Expr(:static_parameter, 1))::Type{T} where T<:AbstractString
│   %2  = invoke Base.Iterators.approx_iter_type(%1::Type)::Type
│   @ iterators.jl:1453 within `Stateful`
│   %3  = $(Expr(:static_parameter, 1))::Type{T} where T<:AbstractString
│   %4  = Core.apply_type(Base.Iterators.Stateful, %3, %2)::Type{Base.Iterators.Stateful{T, T1}} where {T<:AbstractString, T1}
│   %5  = $(Expr(:static_parameter, 1))::Type{T} where T<:AbstractString
│   %6  = (itr isa %5)::Bool
└──       goto #3 if not %6
2 ─       goto #4
3 ─ %9  = $(Expr(:static_parameter, 1))::Type{T} where T<:AbstractString
└── %10 = Base.convert(%9, itr)::Any
4 ┄ %11 = φ (#2 => itr, #3 => %10)::Any
│   %12 = Core.fieldtype(%4, 2)::Type{<:Union{Nothing, T} where T}
│   %13 = Base.Iterators.iterate(itr)::Any
│         Core.typeassert(%13, %2)::Any
│   %15 = (%13 isa %12)::Bool
└──       goto #6 if not %15
5 ─       goto #7
6 ─ %18 = Base.convert(%12, %13)::Any
7 ┄ %19 = φ (#5 => %13, #6 => %18)::Any
│   %20 = %new(%4, %11, %19)::Base.Iterators.Stateful{T} where T<:AbstractString
└──       return %20
) => Base.Iterators.Stateful{T} where T<:AbstractString

@vtjnash vtjnash closed this as completed Aug 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler:inference Type inference
Projects
None yet
Development

No branches or pull requests

6 participants