Skip to content

Commit

Permalink
Vastly improve graph rendering
Browse files Browse the repository at this point in the history
Use unique colors for unique processors
Use unique shapes for each type of processor
Render Comm and Move times for argument-to-thunk edges
  • Loading branch information
jpsamaroo committed Nov 4, 2020
1 parent edcb640 commit 729098d
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 56 deletions.
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ uuid = "d58978e5-989f-55fb-8d15-ea34adc7bf54"
version = "0.10.0"

[deps]
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MemPool = "f9f48841-c794-520a-933b-121f7ba6ed94"
Expand Down
11 changes: 5 additions & 6 deletions src/scheduler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ function fire_task!(ctx, thunk, proc, state, chan, node_order)
end
end

ids = map(thunk.inputs) do x
istask(x) ? x.id : nothing
ids = map(enumerate(thunk.inputs)) do (idx,x)
istask(x) ? x.id : -idx
end
if thunk.meta
# Run it on the parent node, do not move data.
Expand All @@ -285,7 +285,7 @@ function fire_task!(ctx, thunk, proc, state, chan, node_order)

@dbg timespan_start(ctx, :compute, thunk.id, thunk.f)
res = thunk.f(fetched...)
@dbg timespan_end(ctx, :compute, thunk.id, (thunk.f, typeof(res), sizeof(res)))
@dbg timespan_end(ctx, :compute, thunk.id, (thunk.f, p, typeof(res), sizeof(res)))

#push!(state.running, thunk)
state.cache[thunk] = res
Expand Down Expand Up @@ -380,15 +380,14 @@ end

@noinline function do_task(thunk_id, f, data, send_result, persist, cache, options, ids, log_sink)
ctx = Context(Processor[]; log_sink=log_sink)
proc = OSProc()
from_proc = OSProc()
fetched = map(Iterators.zip(data,ids)) do (x, id)
@dbg timespan_start(ctx, :comm, (thunk_id, id), (f, id))
x = x isa Union{Chunk,Thunk} ? collect(ctx, x) : x
@dbg timespan_end(ctx, :comm, (thunk_id, id), (f, id))
return x
end

from_proc = proc
# TODO: Time choose_processor?
to_proc = choose_processor(from_proc, options, f, fetched)
fetched = map(Iterators.zip(fetched,ids)) do (x, id)
Expand All @@ -406,7 +405,7 @@ end
bt = catch_backtrace()
(from_proc, thunk_id, RemoteException(myid(), CapturedException(ex, bt)))
end
@dbg timespan_end(ctx, :compute, thunk_id, (f, typeof(res), sizeof(res)))
@dbg timespan_end(ctx, :compute, thunk_id, (f, to_proc, typeof(res), sizeof(res)))
result_meta
end

Expand Down
162 changes: 112 additions & 50 deletions src/ui/graph.jl
Original file line number Diff line number Diff line change
@@ -1,31 +1,12 @@
import Colors

export show_plan

### DAG-based graphing

function write_node(io, t::Thunk, c)
f = isa(t.f, Function) ? "$(t.f)" : "fn"
println(io, "n_$(t.id) [label=\"$f - $(t.id)\"];")
c
end

dec(x) = Base.dec(x, 0, false)
function write_node(io, t, c, id=dec(hash(t)))
l = replace(node_label(t), "\""=>"")
println(io, "n_$id", " [label=\"$l\"];")
c
end
function node_label(t)
iob = IOBuffer()
Base.show(iob, t)
String(take!(iob))
end
function node_label(x::T) where T<:AbstractArray
"$T\nShape: $(size(x))\nSize: $(pretty_size(sizeof(x)))"
end

global _part_labels = Dict()

function write_node(io, t::Chunk, c)
function write_node(ctx, io, t::Chunk, c)
_part_labels[t]="part_$c"
c+1
end
Expand All @@ -47,7 +28,7 @@ function write_dag(io, t::Thunk)
deps = dependents(t)
c=1
for k in keys(deps)
c = write_node(io, k, c)
c = write_node(nothing, io, k, c)
end
for (k, v) in deps
for dep in v
Expand Down Expand Up @@ -87,88 +68,169 @@ function pretty_size(sz)
end
end

function write_node(io, ts::Timespan, c)
f, res_type, res_sz = ts.timeline
node_label(x) = repr(x)
node_label(x::T) where T<:AbstractArray =
"$T\nShape: $(size(x))\nSize: $(pretty_size(sizeof(x)))"
node_label(x::Chunk) = "Chunk on $(x.processor)"

node_proc(x) = nothing
node_proc(x::Chunk) = x.processor

_proc_color(ctx, proc::Processor) = get!(ctx.proc_to_color, proc) do
_color = ctx.proc_colors[ctx.proc_color_idx[]]
ctx.proc_color_idx[] = clamp(ctx.proc_color_idx[]+1, 0, 128)
"#$(Colors.hex(_color))"
end
_proc_color(ctx, id::Int) = _proc_color(ctx, ctx.id_to_proc[id])
_proc_color(ctx, ::Nothing) = "black"
_proc_shape(ctx, proc::Processor) = get!(ctx.proc_to_shape, typeof(proc)) do
_shape = ctx.proc_shapes[ctx.proc_shape_idx[]]
ctx.proc_shape_idx[] = clamp(ctx.proc_shape_idx[]+1, 0, length(ctx.proc_shapes))
_shape
end
_proc_shape(ctx, ::Nothing) = "ellipse"

function write_node(ctx, io, t::Thunk, c)
f = isa(t.f, Function) ? "$(t.f)" : "fn"
println(io, "n_$(t.id) [label=\"$f - $(t.id)\"];")
c
end

dec(x) = Base.dec(x, 0, false)
function write_node(ctx, io, t, c, id=dec(hash(t)))
l = replace(node_label(t), "\""=>"")
proc = node_proc(t)
color = _proc_color(ctx, proc)
shape = _proc_shape(ctx, proc)
println(io, "n_$id [label=\"$l\",color=\"$color\",shape=\"$shape\",penwidth=5];")
c
end

function write_node(ctx, io, ts::Timespan, c)
f, proc, res_type, res_sz = ts.timeline
f = isa(f, Function) ? "$f" : "fn"
t_comp = pretty_time(ts)
sz_comp = pretty_size(res_sz)
println(io, "n_$(ts.id) [label=\"$f - $(ts.id)\nCompute: $t_comp\nResult Type: $res_type\nResult Size: $sz_comp\"]")
color = _proc_color(ctx, proc)
shape = _proc_shape(ctx, proc)
# TODO: t_log = log(ts.finish - ts.start) / 5
ctx.id_to_proc[ts.id] = proc
println(io, "n_$(ts.id) [label=\"$f\n$t_comp\",color=\"$color\",shape=\"$shape\",penwidth=5];")
# TODO: "\n Thunk $(ts.id)\nResult Type: $res_type\nResult Size: $sz_comp\",
c
end

function write_edge(io, ts_comm::Timespan, logs)
function write_edge(ctx, io, ts_comm::Timespan, logs, inputname=nothing, inputarg=nothing)
f, id = ts_comm.timeline
# FIXME: We should print these edges too
id === nothing && return
t_comm = pretty_time(ts_comm)
print(io, "n_$id -> n_$(ts_comm.id[1]) [label=\"Comm: $t_comm")
if id > 0
print(io, "n_$id -> n_$(ts_comm.id[1]) [label=\"Comm: $t_comm")
color_src = _proc_color(ctx, id)
else
@assert inputname !== nothing
@assert inputarg !== nothing
print(io, "n_$inputname -> n_$(ts_comm.id[1]) [label=\"Comm: $t_comm")
proc = node_proc(inputarg)
color_src = _proc_color(ctx, proc)
end
color_dst = _proc_color(ctx, ts_comm.id[1])
ts_idx = findfirst(x->x.category==:move &&
ts_comm.id==x.id &&
id==x.timeline[2], logs)
ts_comm.id==x.id &&
id==x.timeline[2], logs)
if ts_idx !== nothing
ts_move = logs[ts_idx]
t_move = pretty_time(ts_move)
print(io, "\nMove: $t_move")
end
println(io, "\"];")
#= TODO: log_t = log((ts_comm.finish-ts_comm.start) +
(ts_move.finish-ts_move.start)) / 5=#
println(io, "\",color=\"$color_src;0.5:$color_dst\",penwidth=2];")
end

write_edge(io, from::String, to::String) = println(io, "n_$from -> n_$to")
write_edge(ctx, io, from::String, to::String) = println(io, "n_$from -> n_$to;")

getargs!(d, t) = nothing
function getargs!(d, t::Thunk)
d[t.id] = [filter(!istask, [t.inputs...,])...,]
d[t.id] = [filter(x->!istask(x[2]), collect(enumerate(t.inputs)))...,]
foreach(i->getargs!(d, i), t.inputs)
end
function write_dag(io, logs::Vector, t=nothing)
ctx = (proc_to_color = Dict{Processor,String}(),
proc_colors = Colors.distinguishable_colors(128),
proc_color_idx = Ref{Int}(1),
proc_to_shape = Dict{Type,String}(),
proc_shapes = ("ellipse","box","triangle"),
proc_shape_idx = Ref{Int}(1),
id_to_proc = Dict{Int,Processor}())
argmap = Dict{Int,Vector}()
getargs!(argmap, t)
c = 1
# Compute nodes
for ts in filter(x->x.category==:compute, logs)
c = write_node(io, ts, c)
c = write_node(ctx, io, ts, c)
end
# Argument nodes
argnodemap = Dict{Int,Vector{String}}()
argids = IdDict{Any,String}()
for id in keys(argmap)
nodes = String[]
arg_c = 1
for arg in argmap[id]
name = "$(id)_arg_$(arg_c)"
for (argidx,arg) in argmap[id]
name = "arg_$(argidx)_to_$(id)"
if !isimmutable(arg)
if arg in keys(argids)
name = argids[arg]
else
argids[arg] = name
c = write_node(io, arg, c, name)
c = write_node(ctx, io, arg, c, name)
end
push!(nodes, name)
else
c = write_node(io, arg, c, name)
c = write_node(ctx, io, arg, c, name)
push!(nodes, name)
end
# Arg-to-compute edges
for ts in filter(x->x.category==:comm &&
x.id[1]==id &&
x.timeline[2]==-argidx, logs)
write_edge(ctx, io, ts, logs, name, arg)
end
arg_c += 1
end
argnodemap[id] = nodes
end
for ts in filter(x->x.category==:comm, logs)
write_edge(io, ts, logs)
# Comm+Move edges
for ts in filter(x->x.category==:comm && x.timeline[2]>0, logs)
write_edge(ctx, io, ts, logs)
end
for id in keys(argnodemap)
for arg in argnodemap[id]
write_edge(io, arg, string(id))
end
#= FIXME: Legend (currently it's laid out horizontally)
println(io, """
subgraph {
graph[style=dotted,newrank=true,rankdir=TB];
edge [style=invis];
Legend [shape=box];""")
cur = "Legend"
for id in keys(ctx.id_to_proc)
color = _proc_color(ctx, id)
shape = _proc_shape(ctx, ctx.id_to_proc[id])
name = "proc_$id"
println(io, "$name [color=\"$color\",shape=\"$shape\"];")
println(io, "$cur -> $name;")
cur = name
end
println(io, "}")
=#
end

function show_plan(io::IO, t)
print(io, """digraph {
graph [layout=dot, rankdir=TB];""")
println(io, """strict digraph {
graph [layout=dot,rankdir=LR];""")
write_dag(io, t)
println(io, "}")
end
function show_plan(io::IO, logs::Vector{Timespan}, t::Thunk)
print(io, """digraph {
graph [layout=dot, rankdir=TB];""")
println(io, """strict digraph {
graph [layout=dot,rankdir=LR];""")
write_dag(io, logs, t)
println(io, "}")
end
Expand Down

0 comments on commit 729098d

Please sign in to comment.