StaticArrays forces recompilation of JLLs on Windows with Julia 1.8.1

mkitti
Fri, 23 Sep 2022 21:03:50 +0000
Performance

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:

How to use my CPU's Performance

Use @time_imports. It sounds like you would want to do @time_imports using MSEP Here’s what I get when I do @time_imports using DataFrames, Distributions, FastGaussQuadrature, Gadfly, MixedModels, NamedArrays, QuadGK, StatsFuns, LinearAlgebra julia> @time_imports using DataFrames, Distributions, FastGaussQuadrature, Gadfly, MixedModels, NamedArrays, QuadGK, StatsFuns, LinearAlgebra 0.4 ms Reexport 0.5 ms Compat 15.6 ms OrderedCollections 112.8 ms DataStructures …

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?

19 posts - 5 participants