Skip to content

Commit

Permalink
Add a demo measuring all poorly-inferred backedges
Browse files Browse the repository at this point in the history
These are the scripts used for JuliaLang/julia#36393
  • Loading branch information
timholy committed Jun 30, 2020
1 parent 30b1ee7 commit adaaafc
Show file tree
Hide file tree
Showing 2 changed files with 236 additions and 0 deletions.
107 changes: 107 additions & 0 deletions demos/abstract_analyze_versions.jl
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})
129 changes: 129 additions & 0 deletions demos/abstract_gen_data.jl
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

0 comments on commit adaaafc

Please sign in to comment.