Skip to content

Summary of non-ambiguous patterns of invalidation #35922

Closed
@timholy

Description

@timholy

I've looked at a enough cases, and developed enough tooling, to start getting a better sense for patterns. Here are some tests that load a few packages (just to get away from looking only at Base+stdlibs) and then load a new package (or execute a new command) while capturing invalidations.

Package or command Invalidated sig # children Explanation
FixedPointNumbers reduce_empty(::Function, ::Type{T} where T) 200 Compiler chooses not to specialize this method. Many signatures don't actually intersect with the new method, but lack of specialization makes that hard to detect. A solution: add an init argument to maximum so compiler knows what to do if container is empty.
New ! method !=(::Any, ::Any) & similar 383 Union-splitting for noninferred types. Called by, e.g., convert(::Type{T}, x::AbstractDict) where T<:AbstractDict, cmp(::AbstractString, ::String) ultimately from #15276, handling Expr args, ...
StaticArrays unsafe_convert(::Type{Ptr{Nothing}}, b::Base.RefValue{T}) 290 + tons in other packages Overwhelmingly show_default (primarily show(::IO, ::RawFD) and show_function)
cconvert(::Type{Ptr{Nothing}}, ::Any) 88 REPL.raw! not knowing the handle is Ptr{Cint} (true?)
to_indices(to_indices(A, I::Tuple) 80 inference failures in concatenation
all, any specializations collectively hundreds typically #15276
OffsetArrays axes(::AbstractArray) 240 JuliaInterpreter access to CodeInfo.linetable and CodeInfo.codelocs (both Any)
ColorTypes promote_rule(::Type{<:Any}, ::Type{<:Any}) = Bottom 35 +(::Int, ::Integer) with no backedges
ImageCore convert(::Type{Array{C,N}}, ::Array{C,N} where {C<:Colorant,N} 80 type-piracy, this should be defined in ColorTypes to head off any other packages implementing it
Interpolations Base._getindex(::IndexLinear, A::AbstractVector, i::Int) 44 type-piracy (was defined to resolve an ambiguity)
GeometryTypes (used by Plots) to_index(::Integer) 190 comes from the OffsetInteger type
promote_rule(::Type{Int64}, ::Type{T} where T) 104 again the promote_rule(::Type{<:Any}, ::Type{<:Any}) = Bottom fallback

If one discounts ambiguities, here's a summary:

  • incomplete inference seems by far the most common. None of these are particularly surprising, but for the record:
    • deliberate decisions to avoid specialization may be the single most common source (obviously these are really good for compile times in their own way, but there's a dark side)
    • inference failures, like performance of captured variables in closures #15276, are also very common
    • in some cases non-specialized struct fields trigger problems; in many cases these have an obvious solution, but sometimes this conflicts with the merits of avoiding unnecessary specialization
  • adding methods to intercept default pathways can help (e.g., show methods)
  • type-piracy

Takeaways for the compiler:

  • union-splitting is a big deal for a few cases but it's not actually that common a source
  • perhaps in cases of @nospecialize annotations or where the compiler deliberately chooses not to specialize on an argument type, all calls with non-concrete types should be made by runtime dispatch? EDIT: the reduce_empty case might even need more, if you're calling a method that hasn't been specialized, do it by runtime dispatch?

Something I noticed and perhaps should mention: some MethodInstances appear to have no callers, e.g.,

julia> using MethodAnalysis

julia> mi = instance(Base.require_one_based_indexing, (AbstractArray{Float32,2}, AbstractArray{Float32,1}))
MethodInstance for require_one_based_indexing(::AbstractArray{Float32,2}, ::AbstractArray{Float32,1})

julia> mi.backedges
ERROR: UndefRefError: access to undefined reference
Stacktrace:
 [1] getproperty(::Core.MethodInstance, ::Symbol) at ./Base.jl:33
 [2] top-level scope at REPL[8]:1

I don't understand why this would be compiled if it doesn't have callers. Maybe they already got invalidated? There are quite a lot of these for require_one_based_indexing, to_indices, and things like +(::Int, ::Integer).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions