Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
d19deab
First pass at snoopi collecting the full inferrable set from julia
NHDaly Sep 11, 2020
ac15b22
Update to use bool to control julia "collect inference callees" feature
NHDaly Sep 11, 2020
b9f07fe
Use commit `97b5cde769` from Julia to return dep graph from snoopi
NHDaly Sep 14, 2020
9b5f04a
Almost working parcel per_method_instance_timings -- need to fix seri…
NHDaly Sep 14, 2020
1eb4214
Working per-method timings, i think (though overly complicated and ov…
NHDaly Sep 14, 2020
bb83243
Fix now that i've disabled my first attempt in julia
NHDaly Sep 14, 2020
faa3eb8
Got per-method timing working on helper process! :)
NHDaly Sep 15, 2020
9e441cd
Switch to BFS instead of (recursive) DFS
NHDaly Sep 15, 2020
4cad634
Fix `code_typed()` to `code_typed_by_type(tt)` to ACTUALLY measure in…
NHDaly Sep 16, 2020
6a62f74
Try randomly shuffling the leaves to get a (hopefully) different walk…
NHDaly Sep 16, 2020
4820a24
Second attempt at per-method inf timings: TimerOutputs
NHDaly Sep 17, 2020
21bae6d
(silly) fixups to get the linfos to print to a string
NHDaly Sep 17, 2020
bc20040
Fix double-counting, using "$linfo" for timer name
NHDaly Sep 21, 2020
b290ec4
Start precompiling the TimerOutputs snoopi approach
NHDaly Sep 21, 2020
d77e22d
Revert changes to SnoopCompile snoopi
NHDaly Sep 25, 2020
5b8603a
Rewrite snoopi on top of custom Core.Compiler.Timings
NHDaly Sep 25, 2020
615b1b6
Add `exclusive_timings()` to compute and list the per-method-instance…
NHDaly Sep 25, 2020
bc19fed
Now julia itself reports exclusive times
NHDaly Sep 25, 2020
c91c7d2
Add basic flame graph printing for per-method inference timings!
NHDaly Sep 25, 2020
95b3fc2
Close "root" timer so `root` is an accurate measurement as well! :)
NHDaly Oct 1, 2020
3b34dd6
Split out `@snoopi_deep`: for the per-method-instance snoopi timings
NHDaly Oct 1, 2020
6c9de36
Get `to_flamegraph(::Timing)` working! :grin:
NHDaly Oct 3, 2020
316b8bc
Print snoopi_deep flamegraph with pretty function syntax
NHDaly Oct 3, 2020
fb48e8a
Update to work with latest version of https://github.com/JuliaLang/ju…
NHDaly Oct 10, 2020
0974619
Move `flatten_times()` for at-snoopi_deep to parcel_snoopi.jl
NHDaly Oct 12, 2020
493ae50
Reorganized parcel_snoopi.jl
NHDaly Oct 12, 2020
abfc173
Handle Type Tuples that can't be printed to a string
NHDaly Oct 12, 2020
1e4eda2
Add tests for `@snoopi_deep`, including AbstractTrees tests of FlameG…
NHDaly Oct 12, 2020
1b63149
Add docstring for macro snoopi_deep
NHDaly Oct 12, 2020
3f21529
Add TODO to add `tmin=` to `to_flamegraph()`
NHDaly Oct 13, 2020
52b414e
Add `tmin_secs=` param to `to_flamegraphs()` to filter frames by incl…
NHDaly Oct 13, 2020
1e20614
Switch back to nice MethodInstance printing for frame names!
NHDaly Oct 20, 2020
7bb6a28
Add file and line info to SnoopCompile.to_flamegraph
NHDaly Oct 20, 2020
f1fbd1a
Clean up: Remove debugging and experimentation cruft
NHDaly Oct 20, 2020
2d84f1d
Fix `tmin_secs` for `to_flamegraph()` and `flatten_times()`
NHDaly Oct 20, 2020
3adda6c
Improve docstrings and add Examples
NHDaly Oct 20, 2020
11cab16
Clean up old cruft
NHDaly Oct 20, 2020
54c3462
Add tests for `tmin_secs=` param
NHDaly Oct 20, 2020
ceaecb2
Set fail-fast: false for easier CI for code review
NHDaly Oct 21, 2020
37ad8be
Add compat bounds for FlameGraphs and LeftChildRightSiblingTrees
NHDaly Oct 21, 2020
bfa969e
Apply suggestions from code review
NHDaly Oct 26, 2020
421593d
Fix small type stability of `range=` in `_flamegraph_frame()`
NHDaly Oct 21, 2020
6d3ff30
Simplify Project.toml: Use `FlameGraphs.LeftChildRightSiblingTrees`
NHDaly Oct 26, 2020
74e7f69
Improve docstrings and comments (PR Review suggestions)
NHDaly Oct 26, 2020
4e6b74f
Split out `snoopi_deep` into separate, version-guarded files:
NHDaly Oct 26, 2020
67c447c
Merge branch 'master' into nhd-snoopi-inferrable-set--TimerOutputs-pr…
NHDaly Oct 26, 2020
49ce9f2
Simplify the logic in `SnoopCompile.max_end_time()` and add comments
NHDaly Oct 28, 2020
2723e48
Change `to_flamegraph()` to `flamegraph()`
NHDaly Oct 29, 2020
53d6bf8
Remove qualified names from docstring examples
NHDaly Oct 29, 2020
ba358cc
Update src/parcel_snoopi_deep.jl
NHDaly Oct 29, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ version = "2.1.1"

[deps]
Cthulhu = "f68482b8-f384-11e8-15f7-abe071a5a75f"
FlameGraphs = "08572546-2f56-4bcf-ba4e-bab62c3a3f89"
LeftChildRightSiblingTrees = "1d6d02ad-be62-4b6b-8a6d-2f90e265016e"
Comment thread
NHDaly marked this conversation as resolved.
Outdated
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
Expand Down
72 changes: 69 additions & 3 deletions SnoopCompileCore/src/snoopi.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export @snoopi

const __inf_timing__ = Tuple{Float64,MethodInstance}[]
const __inner_timings__ = Ref{Vector{Any}}()

if isdefined(Core.Compiler, :Params)
function typeinf_ext_timed(linfo::Core.MethodInstance, params::Core.Compiler.Params)
Expand All @@ -17,7 +18,9 @@ if isdefined(Core.Compiler, :Params)
push!(__inf_timing__, (tstop-tstart, linfo))
return ret
end
@noinline stop_timing() = ccall(:jl_set_typeinf_func, Cvoid, (Any,), Core.Compiler.typeinf_ext)
@noinline stop_timing() = begin
ccall(:jl_set_typeinf_func, Cvoid, (Any,), Core.Compiler.typeinf_ext)
end
else
function typeinf_ext_timed(interp::Core.Compiler.AbstractInterpreter, linfo::Core.MethodInstance)
tstart = time()
Expand All @@ -33,10 +36,14 @@ else
push!(__inf_timing__, (tstop-tstart, linfo))
return ret
end
@noinline stop_timing() = ccall(:jl_set_typeinf_func, Cvoid, (Any,), Core.Compiler.typeinf_ext_toplevel)
@noinline stop_timing() = begin
ccall(:jl_set_typeinf_func, Cvoid, (Any,), Core.Compiler.typeinf_ext_toplevel)
end
end

@noinline start_timing() = ccall(:jl_set_typeinf_func, Cvoid, (Any,), typeinf_ext_timed)
@noinline start_timing() = begin
ccall(:jl_set_typeinf_func, Cvoid, (Any,), typeinf_ext_timed)
end

function sort_timed_inf(tmin)
data = __inf_timing__
Expand Down Expand Up @@ -76,6 +83,7 @@ end
function _snoopi(cmd::Expr, tmin = 0.0)
return quote
empty!(__inf_timing__)
Core.Compiler.Timings.reset_timings()
start_timing()
try
$(esc(cmd))
Expand All @@ -86,6 +94,57 @@ function _snoopi(cmd::Expr, tmin = 0.0)
end
end

Comment thread
NHDaly marked this conversation as resolved.
function start_deep_timing()
Core.Compiler.Timings.reset_timings()
Core.Compiler.__set_measure_typeinf(true)
end
function stop_deep_timing()
Core.Compiler.__set_measure_typeinf(false)
Core.Compiler.Timings.close_current_timer()
end

function finish_snoopi_deep()
return Core.Compiler.Timings._timings[1]
end

function _snoopi_deep(cmd::Expr)
return quote
start_deep_timing()
try
$(esc(cmd))
finally
stop_deep_timing()
end
finish_snoopi_deep()
end
end

macro snoopi_deep(cmd)
Comment thread
NHDaly marked this conversation as resolved.
Outdated
return _snoopi_deep(cmd)
end

function flatten_times(timing::Core.Compiler.Timings.Timing, tmin_secs = 0.0)
Comment thread
NHDaly marked this conversation as resolved.
Outdated
tmin_ns = tmin_secs * 1e9
out = Any[]
frontier = [timing]
while !isempty(frontier)
t = popfirst!(frontier)
if t.time < tmin_ns
continue
end
exclusive_time = (t.time / 1e9)
if exclusive_time >= tmin_secs
push!(out, exclusive_time => t.mi_info)
end
for c in t.children
push!(frontier, c)
end
end
return sort(out; by=tl->tl[1])
end



function __init__()
# typeinf_ext_timed must be compiled before it gets run
# We do this in __init__ to make sure it gets compiled to native code
Expand All @@ -99,5 +158,12 @@ function __init__()
end
precompile(start_timing, ())
precompile(stop_timing, ())

@assert precompile(Core.Compiler.Timings.reset_timings, ())
Comment thread
timholy marked this conversation as resolved.
Outdated
@assert precompile(start_deep_timing, ())
@assert precompile(stop_deep_timing, ())
@assert precompile(finish_snoopi_deep, ())

@assert precompile(flatten_times, (Core.Compiler.Timings.Timing, Float64))
nothing
end
96 changes: 96 additions & 0 deletions src/parcel_snoopi.jl
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,99 @@ end
const default_exclusions = Set([
r"\bMain\b",
])


import FlameGraphs
#import AbstractTrees

using Base.StackTraces: StackFrame
using LeftChildRightSiblingTrees: Node, addchild
using Core.Compiler.Timings: Timing

# TODO(PR): It would maybe be better to build up this inclusive timings information
# directly in julia so that we don't have to build a second graph structure? But I wanted
# to keep the amount of work done inside the snooping itself to a minimum? Not sure.
struct InclusiveTiming
name::Any
inclusive_time::UInt64
start_time::UInt64
Comment thread
timholy marked this conversation as resolved.
Outdated
children::Vector{InclusiveTiming}
end
#AbstractTrees.printnode(io::IO, t::InclusiveTiming) = print(io, (t.inclusive_time / 1e9 => t.name))
#AbstractTrees.children(t::InclusiveTiming) = t.children

inclusive_time(t::InclusiveTiming) = t.inclusive_time

function build_inclusive_times(t::Timing)
child_times = InclusiveTiming[
build_inclusive_times(child)
for child in t.children
]
incl_time = t.time + sum(inclusive_time.(child_times); init=UInt64(0))
Comment thread
NHDaly marked this conversation as resolved.
Outdated
return InclusiveTiming(t.mi_info, incl_time, t.start_time, child_times)
end

function frame_name(mi_info::Tuple)
frame_name(mi_info[1]::Core.Compiler.MethodInstance)
end
function frame_name(mi::Core.Compiler.MethodInstance)
frame_name(mi.def.name, mi.specTypes)
end
# Special printing for Type Tuples so they're less ugly in the FlameGraph
function frame_name(name, ::Type{TT}) where TT<:Tuple
#io = IOBuffer()
#Base.show_tuple_as_call(io, name, TT)
#v = String(take!(io))
#if v[1] == '(' # ugh, pprof isn't going to like this leading `(`...
# return string(TT) # return the ugly thing >.<
#else
# return v
#end

# For now, since PProf name mangles strings that parse as functions, we'll just use the
# full Type Tuple string :|
return string(TT)
end

# NOTE: The "root" node doesn't cover th whole profile, because it's only the _complement_
Comment thread
NHDaly marked this conversation as resolved.
Outdated
# of the inference times (so it's missing the _overhead_ from the measurement).
# SO we need to manually create a root node that covers the whole thing.
function max_end_time(t::InclusiveTiming)
return maximum(child.start_time + child.inclusive_time for child in t.children)
end

# Make a flat frame for this Timing
function _flamegraph_frame(to::InclusiveTiming, start_ns; toplevel)
# TODO: Use a better conversion to a StackFrame so this contains the right kind of data
tt = Symbol(frame_name(to.name))
sf = StackFrame(tt, Symbol("none"), 0, nothing, false, false, UInt64(0x0))
status = 0x0 # "default" status -- See FlameGraphs.jl
start = to.start_time - start_ns
if toplevel
# Compute a range over the whole profile for the top node.
range = Int(start) : max_end_time(to) - start_ns
else
range = Int(start) : Int(start + to.inclusive_time)
end
return FlameGraphs.NodeData(sf, status, range)
end

function to_flamegraph(t::Timing)
it = build_inclusive_times(t)
to_flamegraph(it)
end

function to_flamegraph(to::InclusiveTiming)
# Compute a "root" frame for the top-level node, to cover the whole profile
node_data = _flamegraph_frame(to, to.start_time; toplevel=true)
root = Node(node_data)
return _to_flamegraph(to, root, to.start_time)
end
function _to_flamegraph(to::InclusiveTiming, root, start_ns)
for child in to.children
node_data = _flamegraph_frame(child, start_ns; toplevel=false)
node = addchild(root, node_data)
_to_flamegraph(child, node, start_ns)
end
return root
end