Skip to content

Commit ab66f18

Browse files
vtjnashKristofferC
authored andcommitted
stored method interference graph (#58948)
Store full method interference relationship graph in interferences field of Method to avoid expensive morespecific calls during dispatch. This provides significant performance improvements: - Replace method comparisons with precomputed interference lookup. - Optimize ml_matches minmax computation using interference lookups. - Optimize sort_mlmatches for large return sets by iterating over interferences instead of all matching methods. - Add method_morespecific_via_interferences in both C and Julia. This representation may exclude some edges that are implied by transitivity since sort_mlmatches will ensure the correct result by following strong edges. Ambiguous edges are guaranteed to be checkable without recursion. Also fix a variety of bugs along the way: - Builtins signature would cause them to try to discard all other methods during `sort_mlmatches`. - Some ambiguities were over-estimated, which now are improved upon. - Setting lim==-1 now gives the same limited list of methods as lim>0, since that is actually faster now than attempting to give the unsorted list. This provides a better fix to #53814 than #57837 and fixes #58766. - Reverts recent METHOD_SIG_LATEST_HAS_NOTMORESPECIFIC attempt (though not the whole commit), since I found a significant problem with any usage of that bit during testing: it only tracks methods that intersect with a target, but new methods do not necessarily intersect with any existing target. This provides a decent performance improvement to `methods` calls, which implies a decent speed up to package loading also (e.g. ModelingToolkit loads in about 4 seconds instead of 5 seconds). (cherry picked from commit 59a7bb3)
1 parent 2bfc0f2 commit ab66f18

File tree

15 files changed

+638
-529
lines changed

15 files changed

+638
-529
lines changed

base/Base.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,9 @@ include("uuid.jl")
264264
include("pkgid.jl")
265265
include("toml_parser.jl")
266266
include("linking.jl")
267+
module StaticData
267268
include("staticdata.jl")
269+
end
268270
include("loading.jl")
269271

270272
# misc useful functions & macros

base/errorshow.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -441,14 +441,15 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs=[])
441441
line_score = Int[]
442442
# These functions are special cased to only show if first argument is matched.
443443
special = f === convert || f === getindex || f === setindex!
444+
f isa Core.Builtin && return # `methods` isn't very useful for a builtin
444445
funcs = Tuple{Any,Vector{Any}}[(f, arg_types_param)]
445446

446447
# An incorrect call method produces a MethodError for convert.
447448
# It also happens that users type convert when they mean call. So
448449
# pool MethodErrors for these two functions.
449450
if f === convert && !isempty(arg_types_param)
450451
at1 = arg_types_param[1]
451-
if isType(at1) && !has_free_typevars(at1)
452+
if isType(at1) && !has_free_typevars(at1) && at1.parameters[1] isa Type
452453
push!(funcs, (at1.parameters[1], arg_types_param[2:end]))
453454
end
454455
end
@@ -470,8 +471,8 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs=[])
470471
end
471472
sig0 = sig0::DataType
472473
s1 = sig0.parameters[1]
473-
if sig0 === Tuple || !isa(func, rewrap_unionall(s1, method.sig))
474-
# function itself doesn't match or is a builtin
474+
if !isa(func, rewrap_unionall(s1, method.sig))
475+
# function itself doesn't match
475476
continue
476477
else
477478
print(iob, " ")
@@ -616,6 +617,7 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs=[])
616617
println(io) # extra newline for spacing to stacktrace
617618
end
618619
end
620+
nothing
619621
end
620622

621623
# In case the line numbers in the source code have changed since the code was compiled,

base/methodshow.jl

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ end
7878

7979
# NOTE: second argument is deprecated and is no longer used
8080
function kwarg_decl(m::Method, kwtype = nothing)
81-
if m.sig !== Tuple # OpaqueClosure or Builtin
81+
if !(m.sig === Tuple || m.sig <: Tuple{Core.Builtin, Vararg}) # OpaqueClosure or Builtin
8282
kwtype = typeof(Core.kwcall)
8383
sig = rewrap_unionall(Tuple{kwtype, NamedTuple, (unwrap_unionall(m.sig)::DataType).parameters...}, m.sig)
8484
kwli = ccall(:jl_methtable_lookup, Any, (Any, UInt), sig, get_world_counter())
@@ -216,8 +216,7 @@ show(io::IO, ::MIME"text/plain", m::Method; kwargs...) = show_method(io, m; kwar
216216

217217
function show_method(io::IO, m::Method; modulecolor = :light_black, digit_align_width = 1)
218218
tv, decls, file, line = arg_decl_parts(m)
219-
sig = unwrap_unionall(m.sig)
220-
if sig === Tuple
219+
if m.sig <: Tuple{Core.Builtin, Vararg}
221220
# Builtin
222221
print(io, m.name, "(...)")
223222
file = "none"
@@ -420,8 +419,7 @@ end
420419
function show(io::IO, ::MIME"text/html", m::Method)
421420
tv, decls, file, line = arg_decl_parts(m, true)
422421
sig = unwrap_unionall(m.sig)
423-
if sig === Tuple
424-
# Builtin
422+
if sig <: Tuple{Core.Builtin, Vararg}
425423
print(io, m.name, "(...) in ", parentmodule(m))
426424
return
427425
end

base/runtime_internals.jl

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,15 +1330,7 @@ end
13301330
function matches_to_methods(ms::Array{Any,1}, tn::Core.TypeName, mod)
13311331
# Lack of specialization => a comprehension triggers too many invalidations via _collect, so collect the methods manually
13321332
ms = Method[(ms[i]::Core.MethodMatch).method for i in 1:length(ms)]
1333-
# Remove shadowed methods with identical type signatures
1334-
prev = nothing
1335-
filter!(ms) do m
1336-
l = prev
1337-
repeated = (l isa Method && m.sig == l.sig)
1338-
prev = m
1339-
return !repeated
1340-
end
1341-
# Remove methods not part of module (after removing shadowed methods)
1333+
# Remove methods not part of module
13421334
mod === nothing || filter!(ms) do m
13431335
return parentmodule(m) mod
13441336
end

0 commit comments

Comments
 (0)