Skip to content

Commit ff8f48f

Browse files
committed
Improve the display and API
This adds a number of convenience methods and generally adds polish. It also includes a demo script for the analysis used in the blog post.
1 parent d390499 commit ff8f48f

File tree

2 files changed

+97
-11
lines changed

2 files changed

+97
-11
lines changed

examples/invalidations_blog.jl

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using SnoopCompile
2+
using SnoopCompile: countchildren
3+
4+
function hastv(typ)
5+
isa(typ, UnionAll) && return true
6+
if isa(typ, DataType)
7+
for p in typ.parameters
8+
hastv(p) && return true
9+
end
10+
end
11+
return false
12+
end
13+
14+
trees = invalidation_trees(@snoopr using DifferentialEquations)
15+
16+
function summary(trees)
17+
npartial = ngreater = nlesser = nambig = nequal = 0
18+
for methodtree in trees
19+
method = methodtree.method
20+
invs = methodtree.invalidations
21+
for fn in (:mt_backedges, :backedges)
22+
list = getfield(invs, fn)
23+
for (trigger, invtree) in list
24+
sig = isa(trigger, Core.MethodInstance) ? trigger.specTypes : trigger
25+
# if hastv(sig)
26+
# npartial += countchildren(invtree)
27+
# else
28+
ms1, ms2 = method.sig <: sig, sig <: method.sig
29+
if ms1 && !ms2
30+
ngreater += countchildren(invtree)
31+
elseif ms2 && !ms1
32+
nlesser += countchildren(invtree)
33+
elseif ms1 && ms2
34+
nequal += countchildren(invtree)
35+
else
36+
# if hastv(sig)
37+
# npartial += countchildren(invtree)
38+
# else
39+
nambig += countchildren(invtree)
40+
# end
41+
end
42+
# end
43+
end
44+
end
45+
end
46+
@assert nequal == 0
47+
println("$ngreater | $nlesser | $nambig |") # $npartial |")
48+
end
49+
50+
summary(trees)

src/invalidations.jl

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,23 @@ function getroot(node::InstanceTree)
2525
return node
2626
end
2727

28+
function Base.show(io::IO, node::InstanceTree; methods=false)
29+
if get(io, :limit, false)
30+
print(io, node.mi, " at depth ", node.depth, " with ", countchildren(node), " children")
31+
else
32+
println(io, " "^Int(node.depth), methods ? node.mi.def : node.mi)
33+
foreach(node.children) do child
34+
show(io, child; methods=methods)
35+
end
36+
end
37+
end
38+
2839
struct Invalidations
2940
mt_backedges::Vector{Pair{Any,InstanceTree}} # sig=>tree
30-
tables::Vector{MethodInstance}
3141
backedges::Vector{Pair{MethodInstance,InstanceTree}}
42+
mt_cache::Vector{MethodInstance}
3243
end
33-
Invalidations() = Invalidations(Pair{Any,InstanceTree}[], MethodInstance[], InstanceTree[])
44+
Invalidations() = Invalidations(Pair{Any,InstanceTree}[], Pair{MethodInstance,InstanceTree}[], MethodInstance[])
3445

3546
struct MethodInvalidations
3647
method::Method
@@ -61,20 +72,36 @@ countchildren(methinv::MethodInvalidations) = countchildren(methinv.invalidation
6172

6273
# We could use AbstractTrees here, but typically one is not interested in the full tree,
6374
# just the top method and the number of children it has
64-
function Base.show(io::IO, invalidations::Invalidations)
75+
function Base.show(io::IO, invalidations::Invalidations; method=nothing)
6576
iscompact = get(io, :compact, false)
6677
function showlist(io, treelist, indent=0)
6778
n = length(treelist)
6879
for (i, tree) in enumerate(treelist)
80+
sig = nothing
6981
if isa(tree, Pair)
7082
if isa(tree.first, Type)
7183
print(io, "signature ", tree.first, " triggered ")
84+
sig = tree.first
7285
elseif isa(tree.first, MethodInstance)
7386
print(io, tree.first, " triggered ")
87+
sig = tree.first.specTypes
7488
end
7589
tree = tree.second
7690
end
7791
print(io, tree.mi, " (", countchildren(tree), " children)")
92+
if isa(method, Method)
93+
ms1, ms2 = method.sig <: sig, sig <: method.sig
94+
diagnosis = if ms1 && !ms2
95+
"more specific"
96+
elseif ms2 && !ms1
97+
"less specific"
98+
elseif ms1 && ms2
99+
"equal specificity"
100+
else
101+
"ambiguous"
102+
end
103+
printstyled(io, ' ', diagnosis, color=:cyan)
104+
end
78105
if iscompact
79106
i < n && print(io, ", ")
80107
else
@@ -93,15 +120,15 @@ function Base.show(io::IO, invalidations::Invalidations)
93120
end
94121
iscompact && print(io, "; ")
95122
end
96-
if !isempty(invalidations.tables)
97-
println(io, indent, length(invalidations.tables), " mt_cache")
123+
if !isempty(invalidations.mt_cache)
124+
println(io, indent, length(invalidations.mt_cache), " mt_cache")
98125
end
99126
iscompact && print(io, ';')
100127
end
101128

102129
function Base.show(io::IO, methinv::MethodInvalidations)
103130
println(io, methinv.reason, " ", methinv.method, " invalidated:")
104-
show(io, methinv.invalidations)
131+
show(io, methinv.invalidations; method=methinv.method)
105132
end
106133

107134
# `list` is in RPN format, with the "reason" coming after the items
@@ -152,13 +179,15 @@ function invalidation_trees(list)
152179
elseif isa(item, String)
153180
loctag = item
154181
if loctag == "invalidate_mt_cache"
155-
push!(invalidations.tables, mi)
182+
push!(invalidations.mt_cache, mi)
156183
tree = nothing
157184
elseif loctag == "jl_method_table_insert"
158185
push!(invalidations.backedges, mi=>getroot(tree))
159186
tree = nothing
187+
elseif loctag == "insert_backedges"
188+
println("insert_backedges for ", mi)
160189
else
161-
error("unexpected loctag ", loctag)
190+
error("unexpected loctag ", loctag, " at ", i)
162191
end
163192
else
164193
error("unexpected item ", item, " at ", i)
@@ -190,6 +219,13 @@ function invalidation_trees(list)
190219
return sort(methodtrees; by=countchildren)
191220
end
192221

222+
function Base.getindex(methodtree::MethodInvalidations, fn::Symbol)
223+
invs = methodtree.invalidations
224+
return getfield(invs, fn)
225+
end
226+
227+
Base.getindex(methodtree::MethodInvalidations, fn::Symbol, idx::Int) = methodtree[fn][idx]
228+
193229
# function explain(methodtree::MethodInvalidations)
194230
# meth, invalidations = methodtree
195231
# if isa(meth, Method)
@@ -211,7 +247,7 @@ end
211247
# end
212248
# println(" ", countchildren(tree), " direct and indirect descendants")
213249
# end
214-
# for tree in invalidations.tables
250+
# for tree in invalidations.mt_cache
215251
# println(" method table for ", tree.mi)
216252
# println(" ", countchildren(tree), " direct and indirect descendants")
217253
# end
@@ -228,9 +264,9 @@ end
228264
# idx = findfirst(mi, inst.second)
229265
# idx !== nothing && return Any[methodtree, :instances, i, idx...]
230266
# end
231-
# for (i, inst) in enumerate(invalidations.tables)
267+
# for (i, inst) in enumerate(invalidations.mt_cache)
232268
# idx = findfirst(mi, inst)
233-
# idx !== nothing && return Any[methodtree, :tables, i, idx...]
269+
# idx !== nothing && return Any[methodtree, :mt_cache, i, idx...]
234270
# end
235271
# end
236272
# return nothing

0 commit comments

Comments
 (0)