Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid invalidations related to * string concat #2325

Merged
merged 2 commits into from
Feb 13, 2021

Conversation

IanButterworth
Copy link
Member

I noticed that loading Flux was causing Pkg latency, which on investigation seems to be due to ChainRulesCore's necessarily broad * methods (JuliaDiff/ChainRulesCore.jl#273) and the use of * for string concat in Pkg

Before:

julia> using SnoopCompileCore

julia> invalidations = @snoopr using ChainRulesCore;

julia> using SnoopCompile

julia> trees = invalidation_trees(invalidations)
4-element Vector{SnoopCompile.MethodInvalidations}:
 inserting convert(::Type{var"#s4"} where var"#s4"<:Tuple, comp::Composite{var"#s3", var"#s2"} where var"#s2"<:Tuple where var"#s3") in ChainRulesCore at /Users/ian/.julia/packages/ChainRulesCore/cpHLu/src/differentials/composite.jl:68 invalidated:
   mt_backedges: 1: signature Tuple{typeof(convert), Type{Tuple{DataType, DataType, DataType}}, Any} triggered MethodInstance for Pair{DataType, Tuple{DataType, DataType, DataType}}(::Any, ::Any) (0 children)
                 2: signature Tuple{typeof(convert), Type{NTuple{8, DataType}}, Any} triggered MethodInstance for Pair{DataType, NTuple{8, DataType}}(::Any, ::Any) (0 children)
                 3: signature Tuple{typeof(convert), Type{NTuple{7, DataType}}, Any} triggered MethodInstance for Pair{DataType, NTuple{7, DataType}}(::Any, ::Any) (0 children)
                 4: signature Tuple{typeof(convert), Type{Tuple{Symbol, Any, Symbol, Symbol}}, Any} triggered MethodInstance for setindex!(::Vector{Tuple{Symbol, Any, Symbol, Symbol}}, ::Any, ::Int64) (0 children)
                 5: signature Tuple{typeof(convert), Type{Tuple{Base.UUID, String}}, Any} triggered MethodInstance for setindex!(::Vector{Tuple{Base.UUID, String}}, ::Any, ::Int64) (0 children)
                 6: signature Tuple{typeof(convert), Type{Tuple{Any, String}}, Any} triggered MethodInstance for setindex!(::Vector{Tuple{Any, String}}, ::Any, ::Int64) (0 children)

 inserting *(s, comp::Composite) in ChainRulesCore at /Users/ian/.julia/packages/ChainRulesCore/cpHLu/src/differential_arithmetic.jl:138 invalidated:
   mt_backedges: 1: signature Tuple{typeof(*), Union{Regex, String}, Any} triggered MethodInstance for *(::Any, ::Char, ::Any) (0 children)

 inserting convert(::Type{var"#s4"} where var"#s4"<:Dict, comp::Composite{var"#s3", var"#s2"} where var"#s2"<:Dict where var"#s3"<:Dict) in ChainRulesCore at /Users/ian/.julia/packages/ChainRulesCore/cpHLu/src/differentials/composite.jl:69 invalidated:
   mt_backedges: 1: signature Tuple{typeof(convert), Type{Dict{String, Any}}, Any} triggered MethodInstance for setindex!(::Dict{Base.BinaryPlatforms.AbstractPlatform, Dict{String, Any}}, ::Any, ::Base.BinaryPlatforms.Platform) (0 children)
                 2: signature Tuple{typeof(convert), Type{Dict{Symbol, Any}}, Any} triggered MethodInstance for setindex!(::IdDict{Function, Dict{Symbol, Any}}, ::Any, ::Any) (8 children)
                 3: signature Tuple{typeof(convert), Type{Dict{Char, Any}}, Any} triggered MethodInstance for REPL.LineEdit.Prompt(::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any) (18 children)

 inserting *(::Zero, ::Any) in ChainRulesCore at /Users/ian/.julia/packages/ChainRulesCore/cpHLu/src/differential_arithmetic.jl:68 invalidated:
   mt_backedges: 1: signature Tuple{typeof(*), Any, String} triggered MethodInstance for Pkg.REPLMode.promptf() (0 children)
                 2: signature Tuple{typeof(*), Any, String} triggered MethodInstance for Pkg.API.var"#precompile#195"(::Bool, ::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, ::typeof(Pkg.API.precompile), ::Pkg.Types.Context) (15 children)
                 3: signature Tuple{typeof(*), Any, Char} triggered MethodInstance for *(::Any, ::Char, ::Any) (16 children)
   10 mt_cache

The Pkg.API.var"#precompile#195" invalidation seems most substantial

julia> show(last(root); minchildren=0, maxdepth=10)
MethodInstance for Pkg.API.var"#precompile#197"(::Bool, ::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, ::typeof(Pkg.API.precompile), ::Pkg.Types.Context) (15 children)
 MethodInstance for (::Pkg.API.var"#precompile##kw")(::NamedTuple{(:internal_call,), Tuple{Bool}}, ::typeof(Pkg.API.precompile), ::Pkg.Types.Context) (14 children)
  MethodInstance for Pkg.API._auto_precompile(::Pkg.Types.Context) (13 children)
   MethodInstance for Pkg.API.var"#instantiate#249"(::Nothing, ::Bool, ::Bool, ::Base.BinaryPlatforms.Platform, ::Bool, ::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, ::typeof(Pkg.API.instantiate), ::Pkg.Types.Context) (4 children)
    MethodInstance for (::Pkg.API.var"#instantiate##kw")(::NamedTuple{(:manifest, :update_registry, :verbose), Tuple{Nothing, Bool, Bool}}, ::typeof(Pkg.API.instantiate), ::Pkg.Types.Context) (1 children)
     MethodInstance for Pkg.API.var"#instantiate#249"(::Nothing, ::Bool, ::Bool, ::Base.BinaryPlatforms.Platform, ::Bool, ::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, ::typeof(Pkg.API.instantiate), ::Pkg.Types.Context) (0 children)
    MethodInstance for (::Pkg.API.var"#instantiate##kw")(::NamedTuple{(:allow_autoprecomp,), Tuple{Bool}}, ::typeof(Pkg.API.instantiate), ::Pkg.Types.Context) (1 children)
     MethodInstance for Pkg.API.var"#precompile#197"(::Bool, ::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, ::typeof(Pkg.API.precompile), ::Pkg.Types.Context) (0 children)
   MethodInstance for Pkg.API.var"#add#24"(::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, ::typeof(Pkg.API.add), ::Vector{Pkg.Types.PackageSpec}) (7 children)
    MethodInstance for Pkg.API.add(::Vector{Pkg.Types.PackageSpec}) (6 children)
     MethodInstance for Pkg.API.var"#add#22"(::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, ::typeof(Pkg.API.add), ::Pkg.Types.PackageSpec) (1 children)
      MethodInstance for Pkg.API.add(::Pkg.Types.PackageSpec) (0 children)
     MethodInstance for Pkg.API.var"#add#23"(::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, ::typeof(Pkg.API.add), ::Vector{String}) (3 children)
      MethodInstance for Pkg.API.add(::Vector{String}) (2 children)
       MethodInstance for Pkg.API.var"#add#22"(::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, ::typeof(Pkg.API.add), ::String) (1 children)
        MethodInstance for Pkg.API.add(::String) (0 children)

This PR eliminates the Pkg.REPLMode.promptf() and Pkg.API.var"#precompile#195" invalidations

4-element Vector{SnoopCompile.MethodInvalidations}:
 inserting convert(::Type{var"#s4"} where var"#s4"<:Tuple, comp::Composite{var"#s3", var"#s2"} where var"#s2"<:Tuple where var"#s3") in ChainRulesCore at /Users/ian/.julia/dev/ChainRulesCore/src/differentials/composite.jl:68 invalidated:
   mt_backedges: 1: signature Tuple{typeof(convert), Type{Tuple{DataType, DataType, DataType}}, Any} triggered MethodInstance for Pair{DataType, Tuple{DataType, DataType, DataType}}(::Any, ::Any) (0 children)
                 2: signature Tuple{typeof(convert), Type{NTuple{8, DataType}}, Any} triggered MethodInstance for Pair{DataType, NTuple{8, DataType}}(::Any, ::Any) (0 children)
                 3: signature Tuple{typeof(convert), Type{NTuple{7, DataType}}, Any} triggered MethodInstance for Pair{DataType, NTuple{7, DataType}}(::Any, ::Any) (0 children)
                 4: signature Tuple{typeof(convert), Type{Tuple{Symbol, Any, Symbol, Symbol}}, Any} triggered MethodInstance for setindex!(::Vector{Tuple{Symbol, Any, Symbol, Symbol}}, ::Any, ::Int64) (0 children)
                 5: signature Tuple{typeof(convert), Type{Tuple{Any, String}}, Any} triggered MethodInstance for setindex!(::Vector{Tuple{Any, String}}, ::Any, ::Int64) (0 children)

 inserting *(s, comp::Composite) in ChainRulesCore at /Users/ian/.julia/dev/ChainRulesCore/src/differential_arithmetic.jl:138 invalidated:
   mt_backedges: 1: signature Tuple{typeof(*), Union{Regex, String}, Any} triggered MethodInstance for *(::Any, ::Char, ::Any) (0 children)
                 2: signature Tuple{typeof(*), String, Any} triggered MethodInstance for Logging.default_metafmt(::Base.CoreLogging.LogLevel, ::Any, ::Any, ::Any, ::Any, ::Any) (0 children)

 inserting *(::DoesNotExist, ::Any) in ChainRulesCore at /Users/ian/.julia/dev/ChainRulesCore/src/differential_arithmetic.jl:28 invalidated:
   mt_backedges: 1: signature Tuple{typeof(*), Any, Char} triggered MethodInstance for *(::Any, ::Char, ::Any) (16 children)
   8 mt_cache

 inserting convert(::Type{var"#s4"} where var"#s4"<:Dict, comp::Composite{var"#s3", var"#s2"} where var"#s2"<:Dict where var"#s3"<:Dict) in ChainRulesCore at /Users/ian/.julia/dev/ChainRulesCore/src/differentials/composite.jl:69 invalidated:
   mt_backedges: 1: signature Tuple{typeof(convert), Type{Dict{String, Any}}, Any} triggered MethodInstance for setindex!(::Dict{Base.BinaryPlatforms.AbstractPlatform, Dict{String, Any}}, ::Any, ::Base.BinaryPlatforms.Platform) (0 children)
                 2: signature Tuple{typeof(convert), Type{Dict{Symbol, Any}}, Any} triggered MethodInstance for setindex!(::IdDict{Function, Dict{Symbol, Any}}, ::Any, ::Any) (8 children)
                 3: signature Tuple{typeof(convert), Type{Dict{Char, Any}}, Any} triggered MethodInstance for REPL.LineEdit.Prompt(::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any) (18 children)

Copy link
Member

@timholy timholy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I don't know the Pkg internals super-well, so take that FWIW, but overall this looks like a nice enhacement.

@@ -509,7 +509,7 @@ function promptf()
prefix = ""
if project_file !== nothing
if prev_project_file == project_file && prev_project_timestamp == mtime(project_file)
prefix = prev_prefix
prefix = prev_prefix::String
else
project_name = projname(project_file)
if project_name !== nothing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is project_name guaranteed to be a String when it's not nothing? Just wondering about the safety of the typeassert (presumably, you're fine).

Copy link
Member Author

@IanButterworth IanButterworth Jan 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can tell it should be.. either way, wrt. what gets applied to prev_prefix the product of string() is always String, I believe??

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To play it safe I've reverted the type assert

src/API.jl Outdated
@@ -1008,7 +1009,7 @@ function precompile(ctx::Context; internal_call::Bool=false, kwargs...)
end
depsmap = Dict{Base.PkgId, Vector{Base.PkgId}}(Iterators.filter(!isnothing, deps_pair_or_nothing)) #flat map of each dep and its deps

if ctx.env.pkg !== nothing && isfile( joinpath( dirname(ctx.env.project_file), "src", ctx.env.pkg.name * ".jl") )
if ctx.env.pkg !== nothing && isfile( joinpath( dirname(ctx.env.project_file), "src", string(ctx.env.pkg.name, ".jl")) )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the standard way of doing this is:

ctx_env_pkg = ctx.env.pkg
if ctx_env_pkg !== nothing && isfile( joinpath( dirname(ctx.env.project_file), "src", ctx_env_pkg.name * ".jl") )

Basically, hoist the load of the thing we check for nothing so that Julia realizes it cannot have changed the next time we use it. Changing * to string is mostly a bandaid.

@KristofferC
Copy link
Member

If things are well inferred it shouldn't matter if you use * or string, no? So the focus should probably be to change the code to be well inferred, not just change * -> string which will run into the same problems if someone creates similar string methods as was the problem for *

@IanButterworth
Copy link
Member Author

Agreed. I couldn't figure out where the inference issues where, but your pointers help. I'll take a stab at fixing them. Thanks

But I think I'll leave the sprint() changes as they seem more efficient?

@KristofferC
Copy link
Member

But I think I'll leave the sprint() changes as they seem more efficient?

👍

@IanButterworth
Copy link
Member Author

Validated to still eliminate

inserting *(::Zero, ::Any) in ChainRulesCore at /Users/ian/.julia/packages/ChainRulesCore/cpHLu/src/differential_arithmetic.jl:68 invalidated:
   mt_backedges: 1: signature Tuple{typeof(*), Any, String} triggered MethodInstance for Pkg.REPLMode.promptf() (0 children)
                 2: signature Tuple{typeof(*), Any, String} triggered MethodInstance for Pkg.API.var"#precompile#195"(::Bool, ::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}}, ::typeof(Pkg.API.precompile), ::Pkg.Types.Context) (15 children)

@timholy
Copy link
Member

timholy commented Jan 12, 2021

If you provide your versions of Julia & ChainRulesCore I can take a peek. On 1.6 and ChainRulesCore v0.9.25, I'm seeing invalidation of the bodyfunction for API.precompile, mostly likely due to several Core.Box variables (the infamous #15276). See https://timholy.github.io/SnoopCompile.jl/dev/snoopr/#Fixing-Core.Box.

@IanButterworth
Copy link
Member Author

IanButterworth commented Jan 12, 2021

@timholy Sorry, I meant that this PR still fixes (removes) the invalidations I listed, after the review edit commit. Bad choice of wording

src/API.jl Outdated
@@ -114,13 +114,15 @@ end

function check_package_name(x::AbstractString, mode=nothing)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still a bit confused about whether this PR does everything you want it to. If not, one "cheap" way to get better inference is to make the signature check_package_name(x::String, mode::Union{Nothing,SomeType}=nothing) and then also provide an argument-standardizing method

check_package_name(x::AbstractString, mode=nothing) = check_package_name(String(x)::String, convert(Union{Nothing,SomeType}, mode)::Union{Nothing,SomeType})

The typeasserts guarantee you'll never get a StackOverflowError.

@simeonschaub
Copy link
Member

Ah, sorry, I just saw this. This seems to fix the same invalidations as #2376. While that one is more minimal, we might still want to go with this one instead because of the style and performance improvements.

@timholy
Copy link
Member

timholy commented Feb 6, 2021

If @IanButterworth thinks this is good to go, I'm fine with it. I was just confused about what state this was in. From #2376 it looks like there may be Core.Boxes? I generally try to fix those at the source rather than putting bandaids on things.

@IanButterworth
Copy link
Member Author

IanButterworth commented Feb 13, 2021

Sorry @timholy, my understanding of this was a bit fragile and only got back around to it now.

I've narrowed the def of check_package_name re-checked, and it fixes the Pkg invalidations.

REPL.LineEdit.Prompt caught my eye, but seems ok because it's not re-used after the repl is started

julia> using SnoopCompileCore

julia> invalidations = @snoopr using ChainRulesCore;

julia> using SnoopCompile

julia> trees = invalidation_trees(invalidations)
4-element Vector{SnoopCompile.MethodInvalidations}:
 inserting convert(::Type{var"#s4"} where var"#s4"<:Tuple, comp::Composite{var"#s3", var"#s2"} where {var"#s3", var"#s2"<:Tuple}) in ChainRulesCore at /Users/ian/.julia/packages/ChainRulesCore/7d1hl/src/differentials/composite.jl:68 invalidated:
   mt_backedges: 1: signature Tuple{typeof(convert), Type{Tuple{DataType, DataType, DataType}}, Any} triggered MethodInstance for Pair{DataType, Tuple{DataType, DataType, DataType}}(::Any, ::Any) (0 children)
                 2: signature Tuple{typeof(convert), Type{NTuple{8, DataType}}, Any} triggered MethodInstance for Pair{DataType, NTuple{8, DataType}}(::Any, ::Any) (0 children)
                 3: signature Tuple{typeof(convert), Type{NTuple{7, DataType}}, Any} triggered MethodInstance for Pair{DataType, NTuple{7, DataType}}(::Any, ::Any) (0 children)
                 4: signature Tuple{typeof(convert), Type{Tuple{Symbol, Any, Symbol, Symbol}}, Any} triggered MethodInstance for setindex!(::Vector{Tuple{Symbol, Any, Symbol, Symbol}}, ::Any, ::Int64) (0 children)
                 5: signature Tuple{typeof(convert), Type{Tuple{Any, String}}, Any} triggered MethodInstance for setindex!(::Vector{Tuple{Any, String}}, ::Any, ::Int64) (0 children)

 inserting *(s, comp::Composite) in ChainRulesCore at /Users/ian/.julia/packages/ChainRulesCore/7d1hl/src/differential_arithmetic.jl:138 invalidated:
   mt_backedges: 1: signature Tuple{typeof(*), Union{Regex, String}, Any} triggered MethodInstance for *(::Any, ::Char, ::Any) (0 children)

 inserting *(::DoesNotExist, ::Any) in ChainRulesCore at /Users/ian/.julia/packages/ChainRulesCore/7d1hl/src/differential_arithmetic.jl:28 invalidated:
   mt_backedges: 1: signature Tuple{typeof(*), Any, Char} triggered MethodInstance for *(::Any, ::Char, ::Any) (16 children)
   9 mt_cache

 inserting convert(::Type{var"#s4"} where var"#s4"<:Dict, comp::Composite{var"#s3", var"#s2"} where {var"#s3"<:Dict, var"#s2"<:Dict}) in ChainRulesCore at /Users/ian/.julia/packages/ChainRulesCore/7d1hl/src/differentials/composite.jl:69 invalidated:
   mt_backedges: 1: signature Tuple{typeof(convert), Type{Dict{String, Any}}, Any} triggered MethodInstance for setindex!(::Dict{Base.BinaryPlatforms.AbstractPlatform, Dict{String, Any}}, ::Any, ::Base.BinaryPlatforms.Platform) (0 children)
                 2: signature Tuple{typeof(convert), Type{Dict{Symbol, Any}}, Any} triggered MethodInstance for setindex!(::IdDict{Function, Dict{Symbol, Any}}, ::Any, ::Any) (8 children)
                 3: signature Tuple{typeof(convert), Type{Dict{Char, Any}}, Any} triggered MethodInstance for REPL.LineEdit.Prompt(::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any) (18 children)

I'll merge once tests pass

@IanButterworth IanButterworth requested a review from a team as a code owner February 13, 2021 04:35
@IanButterworth IanButterworth merged commit 42fc1bf into JuliaLang:master Feb 13, 2021
@IanButterworth IanButterworth deleted the ib/inv branch February 13, 2021 04:37
IanButterworth added a commit to IanButterworth/Pkg.jl that referenced this pull request Mar 7, 2021
* Avoid invalidations related to * string concat

* use same JULIA_PKG_SERVER setting for processcoverage to ensure registry is functional

(cherry picked from commit 42fc1bf)
@IanButterworth IanButterworth mentioned this pull request Mar 26, 2021
14 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants