From 6643f14e8533ba08d277aee9842d40229cf95895 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sun, 20 Dec 2020 13:18:06 -0600 Subject: [PATCH] Tweaks to reduce latency Investigation via `SnoopCompile.@snoopi_deep` identified several opportunities to reduce latency: - https://github.com/JuliaLang/julia/pull/38906 - https://github.com/JuliaDebug/JuliaInterpreter.jl/pull/461 - https://github.com/JuliaDebug/LoweredCodeUtils.jl/pull/58 Together with the changes here, the aggregate effect is to reduce the time for the first revision from 3.1s to about 1.5s. That's much more reasonable. --- src/lowered.jl | 10 +++++----- src/packagedef.jl | 18 +++++++++++++----- src/precompile.jl | 1 + src/relocatable_exprs.jl | 4 ++-- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/lowered.jl b/src/lowered.jl index 7d6e2b79..ea0370b6 100644 --- a/src/lowered.jl +++ b/src/lowered.jl @@ -55,7 +55,7 @@ function matches_eval(stmt::Expr) (isa(f, GlobalRef) && f.name === :eval) || is_quotenode_egal(f, Core.eval) end -function categorize_stmt(stmt) +function categorize_stmt(@nospecialize(stmt)) ismeth, haseval, isinclude, isnamespace, istoplevel = false, false, false, false, false if isa(stmt, Expr) haseval = matches_eval(stmt) @@ -77,7 +77,7 @@ to `Revise.is_method_or_eval`. Since the contents of such expression are difficult to analyze, it is generally safest to execute all such evals. """ -function minimal_evaluation!(predicate, methodinfo, src::Core.CodeInfo, mode::Symbol) +function minimal_evaluation!(@nospecialize(predicate), methodinfo, src::Core.CodeInfo, mode::Symbol) edges = CodeEdges(src) # LoweredCodeUtils.print_with_code(stdout, src, edges) isrequired = fill(false, length(src.code)) @@ -112,11 +112,11 @@ function minimal_evaluation!(predicate, methodinfo, src::Core.CodeInfo, mode::Sy add_dependencies!(methodinfo, edges, src, isrequired) return isrequired, evalassign end -minimal_evaluation!(predicate, methodinfo, frame::JuliaInterpreter.Frame, mode::Symbol) = +minimal_evaluation!(@nospecialize(predicate), methodinfo, frame::JuliaInterpreter.Frame, mode::Symbol) = minimal_evaluation!(predicate, methodinfo, frame.framecode.src, mode) function minimal_evaluation!(methodinfo, frame, mode::Symbol) - minimal_evaluation!(methodinfo, frame, mode) do stmt + minimal_evaluation!(methodinfo, frame, mode) do @nospecialize(stmt) ismeth, haseval, isinclude, isnamespace, istoplevel = categorize_stmt(stmt) isreq = ismeth | isinclude | istoplevel return mode === :sigs ? (isreq, haseval) : (isreq | isnamespace, haseval) @@ -184,7 +184,7 @@ function methods_by_execution!(@nospecialize(recurse), methodinfo, docexprs, mod end if lwr.head !== :thunk mode === :sigs && return nothing, nothing - return Core.eval(mod, lwr) + return Core.eval(mod, lwr), nothing end frame = JuliaInterpreter.Frame(mod, lwr.args[1]) mode === :eval || LoweredCodeUtils.rename_framemethods!(recurse, frame) diff --git a/src/packagedef.jl b/src/packagedef.jl index 90b83d8d..1c8a049d 100644 --- a/src/packagedef.jl +++ b/src/packagedef.jl @@ -232,6 +232,12 @@ const silencefile = Ref(joinpath(depsdir, "silence.txt")) # Ref so that tests d ## now this is the right strategy.) From the standpoint of CodeTracking, we should ## link the signature to the actual method-defining expression (either :(f() = 1) or :(g() = 2)). +if isdefined(Core, :MethodMatch) + get_method_from_match(mm::Core.MethodMatch) = mm.method +else + get_method_from_match(mm::Core.SimpleVector) = mm[3]::Method +end + function delete_missing!(exs_sigs_old::ExprsSigs, exs_sigs_new) with_logger(_debug_logger) do for (ex, sigs) in exs_sigs_old @@ -242,7 +248,7 @@ function delete_missing!(exs_sigs_old::ExprsSigs, exs_sigs_new) ret = Base._methods_by_ftype(sig, -1, typemax(UInt)) success = false if !isempty(ret) - m = ret[end][3]::Method # the last method returned is the least-specific that matches, and thus most likely to be type-equal + m = get_method_from_match(ret[end]) # the last method returned is the least-specific that matches, and thus most likely to be type-equal methsig = m.sig if sig <: methsig && methsig <: sig locdefs = get(CodeTracking.method_info, sig, nothing) @@ -408,10 +414,12 @@ function add_signature!(methodinfo::CodeTrackingMethodInfo, @nospecialize(sig), locdefs = get(CodeTracking.method_info, sig, nothing) locdefs === nothing && (locdefs = CodeTracking.method_info[sig] = Tuple{LineNumberNode,Expr}[]) newdef = unwrap(methodinfo.exprstack[end]) - if !any(locdef->locdef[1] == ln && isequal(RelocatableExpr(locdef[2]), RelocatableExpr(newdef)), locdefs) - push!(locdefs, (fixpath(ln), newdef)) + if newdef !== nothing + if !any(locdef->locdef[1] == ln && isequal(RelocatableExpr(locdef[2]), RelocatableExpr(newdef)), locdefs) + push!(locdefs, (fixpath(ln), newdef)) + end + push!(methodinfo.allsigs, sig) end - push!(methodinfo.allsigs, sig) return methodinfo end push_expr!(methodinfo::CodeTrackingMethodInfo, mod::Module, ex::Expr) = (push!(methodinfo.exprstack, ex); methodinfo) @@ -452,7 +460,7 @@ end function eval_with_signatures(mod, ex::Expr; mode=:eval, kwargs...) methodinfo = CodeTrackingMethodInfo(ex) docexprs = DocExprs() - _, frame = methods_by_execution!(finish_and_return!, methodinfo, docexprs, mod, ex; mode=mode, kwargs...) + frame = methods_by_execution!(finish_and_return!, methodinfo, docexprs, mod, ex; mode=mode, kwargs...)[2] return methodinfo.allsigs, methodinfo.deps, methodinfo.includes, frame end diff --git a/src/precompile.jl b/src/precompile.jl index 202b7d52..ebaa2d58 100644 --- a/src/precompile.jl +++ b/src/precompile.jl @@ -29,6 +29,7 @@ function _precompile_() MI = CodeTrackingMethodInfo @assert precompile(Tuple{typeof(minimal_evaluation!), MI, Core.CodeInfo, Symbol}) + @assert precompile(Tuple{typeof(minimal_evaluation!), Any, MI, Core.CodeInfo, Symbol}) @assert precompile(Tuple{typeof(methods_by_execution!), Any, MI, DocExprs, Module, Expr}) @assert precompile(Tuple{typeof(methods_by_execution!), Any, MI, DocExprs, JuliaInterpreter.Frame, Vector{Bool}}) @assert precompile(Tuple{typeof(Core.kwfunc(methods_by_execution!)), diff --git a/src/relocatable_exprs.jl b/src/relocatable_exprs.jl index a30bd3c1..26d70f78 100644 --- a/src/relocatable_exprs.jl +++ b/src/relocatable_exprs.jl @@ -98,8 +98,8 @@ function Base.isequal(itera::LineSkippingIterator, iterb::LineSkippingIterator) while true reta === nothing && retb === nothing && return true (reta === nothing || retb === nothing) && return false - vala, ia = reta - valb, ib = retb + vala, ia = reta::Tuple{Any,Int} + valb, ib = retb::Tuple{Any,Int} if isa(vala, Expr) && isa(valb, Expr) vala, valb = vala::Expr, valb::Expr vala.head == valb.head || return false