Eric Davies, @longemen3000, I and others encountered an unexpected result after looking at @time_imports
and discussed it on Slack. Something was forcing JLL packages to recompile a lot of code. We narrowed it down to this minimum working example.
julia> @time_imports using StaticArrays, Rmath_jll
5.4 ms StaticArraysCore
714.3 ms StaticArrays
44.7 ms Preferences
1.2 ms JLLWrappers
391.5 ms Rmath_jll 89.76% compilation time (98% recompilation)
Normally, Rmath_jll only takes a few milliseconds to compile:
julia> @time_imports using Rmath_jll
31.4 ms Preferences
0.5 ms JLLWrappers
3.7 ms Rmath_jll 73.39% compilation time
The import times are still short if we use StaticArraysCore.jl instead of StaticArrays.jl:
julia> @time_imports using StaticArraysCore, Rmath_jll
5.5 ms StaticArraysCore
31.1 ms Preferences
0.6 ms JLLWrappers
5.0 ms Rmath_jll 77.12% compilation time
Here’s an extended example:
To investigate further we used SnoopCompile.jl:
# Setup analysis enviornment
using Pkg
Pkg.activate(; temp = true);
Pkg.add(["StaticArrays", "Rmath_jll", "SnoopCompileCore", "SnoopCompile"])
# Capture method cache invalidation
using SnoopCompileCore
tinf = @snoopr using StaticArrays, Rmath_jll;
using SnoopCompile
The invalidation results are as follows.
julia> length(uinvalidated(tinf))
807
julia> trees = invalidation_trees(tinf)
7-element Vector{SnoopCompile.MethodInvalidations}:
inserting any(f::Function, a::StaticArray; dims) in StaticArrays at C:\Users\mkitti\.julia\packages\StaticArrays\NOLon\src\mapreduce.jl:265 invalidated:
backedges: 1: superseding any(f, itr) in Base at reduce.jl:1191 with MethodInstance for any(::typeof(ismissing), ::Any) (1 children)
2: superseding any(f::Function, a::AbstractArray; dims) in Base at reducedim.jl:1004 with MethodInstance for any(::typeof(ismissing), ::AbstractArray) (1 children)
inserting getproperty(::SOneTo{n}, s::Symbol) where n in StaticArrays at C:\Users\mkitti\.julia\packages\StaticArrays\NOLon\src\SOneTo.jl:57 invalidated:
backedges: 1: superseding getproperty(x, f::Symbol) in Base at Base.jl:38 with MethodInstance for getproperty(::AbstractUnitRange, ::Symbol) (3 children)
inserting instantiate(B::Base.Broadcast.Broadcasted{StaticArraysCore.StaticArrayStyle{M}}) where M in StaticArrays at C:\Users\mkitti\.julia\packages\StaticArrays\NOLon\src\broadcast.jl:30 invalidated:
backedges: 1: superseding instantiate(bc::Base.Broadcast.Broadcasted{Style}) where Style in Base.Broadcast at broadcast.jl:279 with MethodInstance for Base.Broadcast.instantiate(::Base.Broadcast.Broadcasted{Style, Nothing, typeof(Base.wrap_string)} where Style<:Union{Nothing, Base.Broadcast.BroadcastStyle}) (1 children)
2: superseding instantiate(bc::Base.Broadcast.Broadcasted{<:Base.Broadcast.AbstractArrayStyle{0}}) in Base.Broadcast at broadcast.jl:288 with MethodInstance for Base.Broadcast.instantiate(::Base.Broadcast.Broadcasted{Style, Nothing, typeof(Base.wrap_string)} where Style<:Base.Broadcast.AbstractArrayStyle{0}) (4 children)
inserting isassigned(a::StaticArray, i::Int64...) in StaticArrays at C:\Users\mkitti\.julia\packages\StaticArrays\NOLon\src\abstractarray.jl:31 invalidated:
backedges: 1: superseding isassigned(a::AbstractArray, i::Integer...) in Base at abstractarray.jl:563 with MethodInstance for isassigned(::AbstractMatrix, ::Int64, ::Int64) (4 children)
2: superseding isassigned(a::AbstractArray, i::Integer...) in Base at abstractarray.jl:563 with MethodInstance for isassigned(::AbstractVecOrMat, ::Int64, ::Int64) (4 children)
1 mt_cache
inserting (::Base.var"#foldl##kw")(::Any, ::typeof(foldl), op::R, a::StaticArray) where R in StaticArrays at C:\Users\mkitti\.julia\packages\StaticArrays\NOLon\src\mapreduce.jl:222 invalidated:
backedges: 1: superseding (::Base.var"#foldl##kw")(::Any, ::typeof(foldl), op, itr) in Base at reduce.jl:185 with MethodInstance for (::Base.var"#foldl##kw")(::NamedTuple{(:init,), _A} where _A<:Tuple{IOContext}, ::typeof(foldl), ::Type{IOContext}, ::Any) (8 children)
inserting convert(::Type{Array{T, N}}, sa::SizedArray{S, T, N, N, Array{T, N}}) where {S, T, N} in StaticArrays at C:\Users\mkitti\.julia\packages\StaticArrays\NOLon\src\SizedArray.jl:88 invalidated:
mt_backedges: 1: signature Tuple{typeof(convert), Type{Vector{Any}}, Any} triggered MethodInstance for setindex!(::Vector{Vector{Any}}, ::Any, ::Int64) (14 children)
backedges: 1: superseding convert(::Type{T}, a::AbstractArray) where T<:Array in Base at array.jl:617 with MethodInstance for convert(::Type, ::AbstractArray) (1 children)
23 mt_cache
inserting similar(::Type{A}, shape::Union{Tuple{SOneTo, Vararg{Union{Integer, Base.OneTo, SOneTo}}}, Tuple{Union{Integer, Base.OneTo}, SOneTo, Vararg{Union{Integer, Base.OneTo, SOneTo}}}, Tuple{Union{Integer, Base.OneTo}, Union{Integer, Base.OneTo}, SOneTo, Vararg{Union{Integer, Base.OneTo, SOneTo}}}}) where A<:AbstractArray in StaticArrays at C:\Users\mkitti\.julia\packages\StaticArrays\NOLon\src\abstractarray.jl:156 invalidated:
mt_backedges: 1: signature Tuple{typeof(similar), Type{Array{Union{Int64, Symbol}, _A}} where _A, Tuple{Union{Integer, AbstractUnitRange}}} triggered MethodInstance for similar(::Type{Array{Union{Int64, Symbol}, _A}}, ::Union{Integer, AbstractUnitRange}) where _A (0 children)
2: signature Tuple{typeof(similar), Type{Array{Union{Int64, Symbol}, _A}} where _A, Any} triggered MethodInstance for Base._array_for(::Type{Union{Int64, Symbol}}, ::Base.HasShape, ::Any) (0 children)
3: signature Tuple{typeof(similar), Type{BitArray}, Tuple{Union{Integer, AbstractUnitRange}}} triggered MethodInstance for similar(::Type{BitArray}, ::Union{Integer, AbstractUnitRange}) (0 children)
4: signature Tuple{typeof(similar), Type{BitArray}, Any} triggered MethodInstance for similar(::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, _A, typeof(parse)} where _A, ::Type{Bool}, ::Any) (0 children)
5: signature Tuple{typeof(similar), Type{Array{Any, _A}} where _A, Tuple{Union{Integer, AbstractUnitRange}}} triggered MethodInstance for similar(::Type{Array{Any, _A}}, ::Union{Integer, AbstractUnitRange}) where _A (0 children)
6: signature Tuple{typeof(similar), Type{Array{Any, _A}} where _A, Any} triggered MethodInstance for Base._array_for(::Type{Any}, ::Base.HasShape, ::Any) (0 children)
7: signature Tuple{typeof(similar), Type{Array{Base.PkgId, _A}} where _A, Tuple{Union{Integer, AbstractUnitRange}}} triggered MethodInstance for similar(::Type{Array{Base.PkgId, _A}}, ::Union{Integer, AbstractUnitRange}) where _A (0 children)
8: signature Tuple{typeof(similar), Type{Array{Base.PkgId, _A}} where _A, Any} triggered MethodInstance for Base._array_for(::Type{Base.PkgId}, ::Base.HasShape, ::Any) (0 children)
9: signature Tuple{typeof(similar), Type{Array{Union{Int64, Symbol}, _A}} where _A, Tuple{Union{Integer, AbstractUnitRange}}} triggered MethodInstance for similar(::Type{Array{Union{Int64, Symbol}, _A}}, ::Tuple{Union{Integer, Base.OneTo}}) where _A (9 children)
10: signature Tuple{typeof(similar), Type{Array{Base.PkgId, _A}} where _A, Tuple{Union{Integer, AbstractUnitRange}}} triggered MethodInstance for similar(::Type{Array{Base.PkgId, _A}}, ::Tuple{Union{Integer, Base.OneTo}}) where _A (9 children)
11: signature Tuple{typeof(similar), Type{Array{Any, _A}} where _A, Tuple{Union{Integer, AbstractUnitRange}}} triggered MethodInstance for similar(::Type{Array{Any, _A}}, ::Tuple{Union{Integer, Base.OneTo}}) where _A (10 children)
12: signature Tuple{typeof(similar), Type{BitArray}, Tuple{Union{Integer, AbstractUnitRange}}} triggered MethodInstance for similar(::Type{BitArray}, ::Tuple{Union{Integer, Base.OneTo}}) (1250 children)
3 mt_cache
In case, it gets lost I would like to highlight that last signature that has 1250 children:
12: signature Tuple{typeof(similar), Type{BitArray}, Tuple{Union{Integer, AbstractUnitRange}}} triggered MethodInstance for similar(::Type{BitArray}, ::Tuple{Union{Integer, Base.OneTo}}) (1250 children)
This issue was recently discussed by @ChrisRackauckas in his recent blog post involving package splitting. This also drove the creation of StaticArraysCore.jl by @oschulz and @mateuszbaran.
While using StaticArraysCore.jl in most packages seems like a temporary solution, the larger issue seems to be that StaticArrays.jl is able to invalidate so much.
I tried further analysis but got lost in the large tree.
julia> method_invalidations = trees[end];
julia> root = method_invalidations.mt_backedges[end].second
MethodInstance for similar(::Type{BitArray}, ::Tuple{Union{Integer, Base.OneTo}}) at depth 0 with 1250 children
julia> ascend(root)
@tim.holy Is there a way to contain the invalidations?