First of all, thank you for the awesome tooling around this. I appreciate there isn’t a straightforward answer to each case.
I’ve re-read and re-watched a few times:
- the blog post on invalidations and the post
- SnoopCompile.jl docs on invalidations
- analyzing and fixing invalidations
- and the discussion on invalidations in GMT
It all makes sense when explained, but I’m struggling to convert my understanding into actions on real-life examples (eg, when to invokelatest
, add concrete type annotations to caller body, open an issue with an upstream pkg or even with Julia Base)
Is there a resource that I missed / that I should look into?
Could you perhaps comment on what you would do in the following cases?
I’ve been looking at precompiling some Genie/Stipple-based apps and noticed a few cases where I didn’t know what the right next step would be.
Using the standard snippet (awesome idea to have this copy&paste-able snippet!):
using SnoopCompileCore
invalidations = @snoopr begin
using GenieFramework
end
using SnoopCompile
length(uinvalidated(invalidations))
trees = invalidation_trees(invalidations)
methinvs = trees[end];
root = methinvs.backedges[end]
ascend(root)
1) Example with unlock(io)
On the last record (most invalidations), I get
methinvs = trees[end]
inserting unlock(io::GarishPrint.GarishIO) @ GarishPrint ~/.julia/packages/GarishPrint/214Ip/src/io.jl:153 invalidated:
backedges: 1: superseding unlock(::IO) @ Base io.jl:27 with MethodInstance for unlock(::IO) (385 children)
6 mt_cache
I hit the ascend and jump to the last concrete call
julia> ascend(root)
Choose a call for analysis (q to quit):
unlock(::IO)
print(::IO, ::String, ::String)
println(::IO, ::String)
> println(::String)
deep_clean!(::Pkg.Resolve.Graph)
#simplify_graph!#117(::Bool, ::typeof(Pkg.Resolve.simplify_graph!), ::Pkg.Resolve.Graph, ::Set{Int64})
simplify_graph!(::Pkg.Resolve.Graph, ::Set{Int64})
simplify_graph!(::Pkg.Resolve.Graph)
trigger_failure!(::Pkg.Resolve.Graph, ::Vector{Int64}, ::Tuple{Int64, Int64})
v _resolve(::Pkg.Resolve.Graph, ::Nothing, ::Nothing)
# that shows
println(xs...) @ Base ~/.julia/juliaup/julia-1.9.0-rc3+0.aarch64.apple.darwin14/share/julia/base/coreio.jl:4
4 println(xs::Tuple{String}...)::Core.Const(nothing) = println(stdout::Any, xs::Tuple{String}...)
It tells me that the inference fails because Any comes from printing into stdout
.
That’s confusing because:
- it’s extremely common, so I would expect problems everywhere
- I thought
stdout
is a concrete type (Base.TTY) - the inference correctly identifies that all the returns are nothing
Perhaps I shouldn’t be looking at the backedges, so I looked at the code that inserted the new method, but that’s defined on a concrete type.
It doesn’t feel like “stomping out the Any type” applies here.
2) Example with peek(io)
After the failure with the last “tree”, I move to the one before last
julia> ascend(trees[end-1].backedges[end])
Choose a call for analysis (q to quit):
peek(::IO)
read(::IO, ::Type{Char})
iterate(::Base.ReadEachIterator{Char}, ::Nothing)
iterate(::Base.ReadEachIterator{Char})
> #readuntil#433(::Bool, ::typeof(readuntil), ::IO, ::Char)
readuntil(::IO, ::Char)
#readuntil#411(::Base.Pairs, ::typeof(readuntil), ::Base.AbstractPipe, ::Char)
readuntil(::Base.AbstractPipe, ::Char)
#readuntil#411(::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, ::typeof(re
v readuntil(::Base.Process, ::Char)
So I descend into the last line with Base.Process, as it’s the last concrete call and I ultimately end up with:
readuntil((pipe_reader(io::Base.AbstractPipe)::Any::IO::Type{IO})::IO, arg::Char; kw::Base.Pairs{Symbol,…
I’m bit lost because I have no idea how to concretize what will come out of that pipe.
That’s another failure on my part.
3) Example with convert(T,…)
So I clearly have no idea when it comes to IO, so I figured to jump to convert
function next, because I have noticed that it features in every single attempt I’ve made at analyzing invalidations (across many different packages).
So I jump into:
inserting convert(::Type{T}, x::EzXML.ReaderType) where T<:Integer @ EzXML ~/.julia/packages/EzXML/ZNwhK/src/streamreader.jl:54 invalidated:
backedges: 1: superseding convert(::Type{T}, x::Number) where T<:Number @ Base number.jl:7 with MethodInstance for convert(::Type{Int64}, ::Integer) (22 children)
2: superseding convert(::Type{T}, x::Number) where T<:Number @ Base number.jl:7 with MethodInstance for convert(::Type{UInt64}, ::Integer) (28 children)
3: superseding convert(::Type{T}, x::Number) where T<:Number @ Base number.jl:7 with MethodInstance for convert(::Type{UInt64}, ::Integer) (1 children)
4: superseding convert(::Type{T}, x::Number) where T<:Number @ Base number.jl:7 with MethodInstance for convert(::Type{UInt64}, ::Integer) (7 children)
5: superseding convert(::Type{T}, x::Number) where T<:Number @ Base number.jl:7 with MethodInstance for convert(::Type{Int8}, ::Integer) (40 children)
6: superseding convert(::Type{T}, x::Number) where T<:Number @ Base number.jl:7 with MethodInstance for convert(::Type{UInt64}, ::Integer) (10 children)
7: superseding convert(::Type{T}, x::Number) where T<:Number @ Base number.jl:7 with MethodInstance for convert(::Type{Int64}, ::Integer) (68 children)
And descript scrolling to the bottom of the list, I haven’t found any line where the “reds” (abstract types) start:
julia> ascend(trees[end-4].backedges[end])
Choose a call for analysis (q to quit):
^ to_index(::Integer)
to_index(::Vector{Int64}, ::Integer)
_to_indices1(::Vector{Int64}, ::Tuple{}, ::Integer)
to_indices(::Vector{Int64}, ::Tuple{}, ::Tuple{Integer})
to_indices(::Vector{Int64}, ::Tuple{Integer})
setindex!(::Vector{Int64}, ::Int64, ::Union{Integer, CartesianIndex})
sortperm_int_range(::Vector{T} where T<:Integer, ::Any, ::Any)
> #_sortperm#33(::Base.Sort.MissingOptimization{Base.Sort.BoolOptimization{Base.Sort.Small{10
kwcall(::NamedTuple{(:alg, :order, :scratch), Tuple{Base.Sort.MissingOptimization{Base.So
v #sortperm#32(::Base.Sort.MissingOptimization{Base.Sort.BoolOptimization{Base.Sort.Small
...
> to_index(::Vector{Symbol}, ::Number)
_to_indices1(::Vector{Symbol}, ::Tuple{Base.OneTo{Int64}}, ::Number)
to_indices(::Vector{Symbol}, ::Tuple{Base.OneTo{Int64}}, ::Tuple{Number})
to_indices(::Vector{Symbol}, ::Tuple{Number})
getindex(::Vector{Symbol}, ::Number)
maybeview(::Vector{Symbol}, ::Union{Number, Base.AbstractCartesianIndex})
Apologies for the long post, but would you have any tips / advice how to:
- proceed in the above cases?
- identify which cases are “easier” to fix for people learning about invals. (eg, “avoid all convert()”, “avoid io”, etc.)
Thank you!