Closed
Description
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: thereduce_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)
.