-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a demo measuring all poorly-inferred backedges
These are the scripts used for JuliaLang/julia#36393
- Loading branch information
Showing
2 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
using PyPlot | ||
|
||
function parseline(line) | ||
m = match(r"(.*) => (.*)", line) | ||
sigstr, count = m.captures[1], parse(Int, m.captures[2]) | ||
sig = try | ||
ex = Meta.parse(sigstr) | ||
eval(ex) | ||
catch | ||
return nothing | ||
end | ||
return sig, count | ||
end | ||
|
||
function parsedata(filename) | ||
lines = readlines(filename) | ||
sigcount = IdDict{Any,Int}() | ||
for line in lines | ||
ret = parseline(line) | ||
ret === nothing && continue | ||
sig, count = ret | ||
sigcount[sig] = count | ||
end | ||
return sigcount | ||
end | ||
|
||
function split_comparable(sigc1, sigc2) | ||
c1, c2, sigs = Int[], Int[], Any[] | ||
for (sig, c) in sigc1 | ||
push!(sigs, sig) | ||
push!(c1, sigc1[sig]) | ||
push!(c2, get(sigc2, sig, 0)) | ||
end | ||
for (sig, c) in sigc2 | ||
if !haskey(sigc1, sig) | ||
push!(sigs, sig) | ||
push!(c1, 0) | ||
push!(c2, c) | ||
end | ||
end | ||
return sigs, c1, c2 | ||
end | ||
|
||
sigc16 = parsedata("/tmp/methdata_$VERSION.log") | ||
sigc14 = parsedata("/tmp/methdata_1.4.3-pre.0.log") | ||
|
||
sigs, c1, c2 = split_comparable(sigc14, sigc16) | ||
mx1, mx2 = maximum(c1), maximum(c2) | ||
isexported(sig) = (ft = Base.unwrap_unionall(sig).parameters[1]; isdefined(Main, ft.name.mt.name)) | ||
colors = [isexported(sig) ? "magenta" : "green" for sig in sigs] | ||
|
||
function on_click(event) | ||
x, y = event.xdata, event.ydata | ||
normsqrdist(pr) = ((pr[1]-x)/mx1)^2 + ((pr[2]-y)/mx2)^2 | ||
idx = argmin(normsqrdist.(zip(c1, c2))) | ||
println(sigs[idx]) | ||
end | ||
begin | ||
fig, ax = plt.subplots() | ||
ax.scatter(c1 .+ 1, c2 .+ 1, c=colors) # + 1 for the log-scaling | ||
ax.set_xlabel("# backedges + 1, 1.4") | ||
ax.set_ylabel("# backedges + 1, 1.6") | ||
ax.set_xscale("log") | ||
ax.set_yscale("log") | ||
fig.canvas.callbacks.connect("button_press_event", on_click) | ||
fig | ||
end | ||
|
||
# Ones we've made progress on: | ||
# ==(::Any, Symbol) | ||
# ==(::Symbol, ::Any) | ||
# ==(::Any, ::Nothing) | ||
# ==(::UUID, ::Any) | ||
# ==(::AbstractString, ::String) | ||
# isequal(::Symbol, ::Any) | ||
# isequal(::Any, ::Symbol) | ||
# isequal(::Any, ::Nothing) | ||
# isequal(::UUID, ::Any) | ||
# cmp(::AbstractString, ::String) | ||
# convert(::Type{Int}, ::Integer) | ||
# convert(::Type{UInt}, ::Integer) | ||
# convert(::Type{Union{Nothing,Module}}, ::Any) | ||
# Base.to_index(::Integer) | ||
# iterate(::Base.OneTo, ::Any) | ||
# repr(::Any) | ||
# thisind(::AbstractString, ::Int) | ||
# getindex(::String, ::Any) | ||
# string(::String, ::Integer, ::String) | ||
# ^(::String, ::Integer) | ||
# repeat(::String, ::Integer) | ||
# Base.isidentifier(::AbstractString) | ||
# +(::Ptr{UInt8}, ::Integer) | ||
# Base._show_default(::Base.GenericIOBuffer{Array{UInt8,1}}, ::Any) | ||
|
||
# Ones that are better but I don't remember helping with | ||
# isconcretetype(::Any) | ||
# pointer(::String, ::Integer) | ||
|
||
# Regressions: | ||
# basename(::AbstractString) | ||
# splitdir(::AbstractString) | ||
# isfile(::Any) | ||
# joinpath(::AbstractString, ::String) | ||
# sizeof(::Unsigned) | ||
# +(::Int, ::Any, ::Any) | ||
# Base.split_sign(::Integer) | ||
# in(::Any, ::Tuple{Symbol}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
using MethodAnalysis | ||
|
||
# Analyze MethodInstance signatures and select those that seem at risk for being invalidated. | ||
function atrisktype(@nospecialize(typ)) | ||
# signatures like `convert(Vector, a)`, `foo(::Vararg{Synbol,N}) where N` do not seem to pose a problem | ||
isa(typ, TypeVar) && return false | ||
# isbits parameters are not a problem | ||
isa(typ, Type) || return false | ||
if isa(typ, UnionAll) | ||
typ = Base.unwrap_unionall(typ) | ||
end | ||
# Exclude signatures with Union{} | ||
typ === Union{} && return false | ||
isa(typ, Union) && return atrisktype(typ.a) | atrisktype(typ.b) | ||
# Type{T}: signatures like `convert(::Type{AbstractString}, ::String)` are not problematic, so mark Type as OK | ||
typ <: Type && return false | ||
if typ <: Tuple && length(typ.parameters) >= 1 | ||
p1 = typ.parameters[1] | ||
# Constructor calls are not themselves a problem (any `convert`s they trigger might be, but those are covered) | ||
isa(p1, Type) && p1 <: Type && return false | ||
# convert(::Type{T}, ::S) where S<:T is not problematic | ||
if p1 === typeof(Base.convert) || p1 === typeof(Core.convert) || p1 === typeof(Core.Compiler.convert) | ||
p2, p3 = typ.parameters[2], typ.parameters[3] | ||
if isa(p2, Type) | ||
p2 = Base.unwrap_unionall(p2) | ||
if isa(p2, DataType) && length(p2.parameters) === 1 | ||
T = p2.parameters[1] | ||
isa(p3, Type) && isa(T, Type) && p3 <: T && return false | ||
end | ||
end | ||
# `getindex`, `length`, etc are OK for various Tuple{T1,T2,...} | ||
elseif (p1 === typeof(Base.getindex) || p1 === typeof(Core.Compiler.getindex)) || | ||
(p1 === typeof(Base.length) || p1 === typeof(Core.Compiler.length)) || | ||
(p1 === typeof(Base.isempty) || p1 === typeof(Core.Compiler.isempty)) || | ||
(p1 === typeof(Base.iterate) || p1 === typeof(Core.iterate) || p1 === typeof(Core.Compiler.iterate)) | ||
p2 = typ.parameters[2] | ||
if isa(p2, Type) | ||
p2 = Base.unwrap_unionall(p2) | ||
p2 <: Tuple && return false | ||
end | ||
# show(io::IO, x) is OK as long as typeof(x) is safe | ||
elseif p1 === typeof(Base.show) | ||
atrisktype(typ.parameters[2]) && return true | ||
length(typ.parameters) == 3 && atrisktype(typ.parameters[3]) && return true | ||
return false | ||
end | ||
end | ||
# Standard DataTypes | ||
isconcretetype(typ) && return false | ||
# ::Function args are excluded | ||
typ === Function && return false | ||
!isempty(typ.parameters) && (any(atrisktype, typ.parameters) || return false) | ||
return true | ||
end | ||
|
||
# A few tests | ||
@assert atrisktype(Tuple{typeof(==),Any,Any}) | ||
@assert atrisktype(Tuple{typeof(==),Symbol,Any}) | ||
@assert atrisktype(Tuple{typeof(==),Any,Symbol}) | ||
@assert !atrisktype(Tuple{typeof(==),Symbol,Symbol}) | ||
@assert !atrisktype(Tuple{typeof(convert),Type{Any},Any}) | ||
@assert !atrisktype(Tuple{typeof(convert),Type{AbstractString},AbstractString}) | ||
@assert !atrisktype(Tuple{typeof(convert),Type{AbstractString},String}) | ||
@assert atrisktype(Tuple{typeof(convert),Type{String},AbstractString}) | ||
@assert !atrisktype(Tuple{typeof(map),Function,Vector{Any}}) | ||
@assert !atrisktype(Tuple{typeof(getindex),Dict{Union{String,Int},Any},Union{String,Int}}) | ||
@assert atrisktype(Tuple{typeof(getindex),Dict{Union{String,Int},Any},Any}) | ||
@assert !atrisktype(Tuple{Type{BoundsError},Any,Any}) | ||
@assert atrisktype(Tuple{typeof(sin),Any}) | ||
@assert !atrisktype(Tuple{typeof(length),Tuple{Any,Any}}) | ||
|
||
function collect_mis(m::Method) | ||
list = Core.MethodInstance[] | ||
visit(m) do item | ||
if isa(item, Core.MethodInstance) | ||
push!(list, item) | ||
return false | ||
end | ||
return true | ||
end | ||
return list | ||
end | ||
|
||
const mis = Dict{Method,Vector{Core.MethodInstance}}() | ||
visit() do item | ||
if item isa Method | ||
m = item | ||
mis[m] = collect_mis(m) | ||
return false | ||
end | ||
return true | ||
end | ||
|
||
# Count # of backedges for MethodInstances with abstract types | ||
const becounter = Dict{Core.MethodInstance,Int}() | ||
visit() do item | ||
if item isa Core.MethodInstance | ||
if atrisktype(item.specTypes) | ||
becounter[item] = length(all_backedges(item)) | ||
end | ||
return false | ||
end | ||
return true | ||
end | ||
|
||
prs = sort!(collect(becounter); by=last) | ||
|
||
# Organize them by method instead | ||
|
||
const mcounter = Dict{Method,Int}() | ||
for (mi, c) in becounter | ||
oc = get(mcounter, mi.def, 0) | ||
mcounter[mi.def] = oc + c | ||
end | ||
|
||
mprs = sort!(collect(mcounter); by=last) | ||
|
||
open("/tmp/methinstdata_$VERSION.log", "w") do io | ||
for (mi, c) in prs | ||
c == 0 && continue | ||
println(io, mi.specTypes=>c) | ||
end | ||
end | ||
open("/tmp/methdata_$VERSION.log", "w") do io | ||
for (m, c) in mprs | ||
c == 0 && continue | ||
println(io, m.sig=>c) | ||
end | ||
end |