Skip to content

Commit

Permalink
Rewrite tree-pruning algorithm
Browse files Browse the repository at this point in the history
I was experiencing trouble from collapsing C functions that called
Julia functions. This rewrites the algorithm so it progresses first
to the leaves and then works its way back inwards.
  • Loading branch information
timholy committed Jul 30, 2013
1 parent c5ded64 commit 42314a2
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 48 deletions.
8 changes: 8 additions & 0 deletions LICENSE.txt
@@ -0,0 +1,8 @@
The MIT License (MIT)
Copyright (c) 2013 Timothy E. Holy

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
95 changes: 91 additions & 4 deletions src/ProfileView.jl
Expand Up @@ -9,7 +9,7 @@ using .PVTree
using Tk, Color, Base.Graphics
import Cairo

import Base: isequal, show
import Base: contains, isequal, show

# TODO: implement zoom

Expand Down Expand Up @@ -56,10 +56,28 @@ function view(data = Profile.fetch(); C = false, colorgc = true, fontsize = 12,
w = sum(counts)
root = Tree.Node(PVData(1:w))
PVTree.buildgraph!(root, bt, counts, 0, ip2so, so2ip, lidict)
PVTree.setstatus!(root, isgc)
# Tree.showedges(STDOUT, root, x -> string(get(lidict, x.ip, "root"), ", hspan = ", x.hspan, ", status = ", x.status))
PVTree.prunegraph!(root, C, isjl, isgc)
# Tree.showedges(STDOUT, root, x -> string(get(lidict, x.ip, "root"), ", status = ", x.status))
# Tree.showedges(STDOUT, root, x -> x.status == 0 ? nothing : string(get(lidict, x.ip, "root"), ", status = ", x.status))
# checkidentity(ip2so, so2ip)
# checkcontains(root, ip2so, so2ip, lidict)
# checkstatus(root, isgc, isjl, C, lidict)
counts = zeros(Int, length(uip))
if !C
PVTree.prunegraph!(root, isjl, lidict, ip2so, counts)
end
# for ip in uip
# println(counts[ip2so[ip]], ": ", lidict[ip])
# end
# if !C
# havegc = any([isgc[ip] for ip in uip])
# if havegc
# @assert checkprunedgc(root, false)
# end
# end
# println("\nPruned:")
# Tree.showedges(STDOUT, root, x -> string(get(lidict, x.ip, "root"), " status = ", x.status))
# Tree.showedges(STDOUT, root, x -> string(get(lidict, x.ip, "root"), ", status = ", x.status))
# Generate a "tagged" image
rowtags = {fill(TAGNONE, w)}
buildtags!(rowtags, root, 1)
Expand Down Expand Up @@ -167,7 +185,6 @@ function buildimg(imgtags, colors, bkg, gccolor, colorgc::Bool, combine::Bool, l
for i = 1:w
t = imgtags[i,j]
if t != TAGNONE
status |= t.status
if t != lasttag && (lasttag == TAGNONE || !(combine && lidict[lasttag.ip] == lidict[t.ip]))
if first != 0
colorindex = fillrow!(img, j, first:i-1, colorindex, colorlen, nextcolor, gccolor, status & colorgc)
Expand All @@ -176,6 +193,8 @@ function buildimg(imgtags, colors, bkg, gccolor, colorgc::Bool, combine::Bool, l
end
first = i
lasttag = t
else
status |= t.status
end
else
if first != 0
Expand Down Expand Up @@ -205,4 +224,72 @@ function fillrow!(img, j, rng::Range1{Int}, colorindex, colorlen, regcolor, gcco
end
end

#### Debugging code

function checkidentity(ip2so, so2ip)
for (k,v) in ip2so
@assert so2ip[v] == k
end
end

function checkcontains(root, ip2so, so2ip, lidict)
flag = contains(root, ip2so)
if !all(flag)
missing = find(!flag)
println("missing ips:")
for i in missing
@show i
@show so2ip[i]
println(lidict[so2ip[i]])
end
error("Internal error: the tree does not contain all ips")
end
end

# This skips the parent, gets everything else
# (to avoid a problem with root with ip=0)
function contains(parent::Node, ip2so::Dict)
ret = Array(Bool, 0)
contains!(ret, parent, ip2so)
@show length(ip2so)
@show length(ret)
return ret
end

function contains!(ret, parent::Node, ip2so::Dict)
for c in parent
indx = ip2so[c.data.ip]
setindexsafe!(ret, indx, true)
contains!(ret, c, ip2so)
end
end

function setindexsafe!(a, i::Integer, val)
if i > length(a)
insert!(a, i, val)
else
a[i] = val
end
end

function checkstatus(parent::Node, isgc::Dict, isjl::Dict, C, lidict)
if isgc[parent.data.ip] && parent.data.status == 0
@show lidict[parent.data.ip]
error("gc should be set, and it isn't")
end
for c in parent
checkstatus(c, isgc, isjl, C, lidict)
end
end

function checkprunedgc(parent::Node, tf::Bool)
tf |= parent.data.status > 0
if !tf
for c in parent
tf = checkprunedgc(c, tf)
end
end
tf
end

end
74 changes: 44 additions & 30 deletions src/pvtree.jl
Expand Up @@ -80,43 +80,57 @@ function buildgraph!(parent::Node, bt::Vector{Vector{Uint}}, counts::Vector{Int}
end
end

# Updates the status and, if C == false, prunes any "C" children
function prunegraph!(parent::Node, C::Bool, isjl::Dict, isgc::Dict)
pisjl = isjl[parent.data.ip]
firstchild = true
prevchild = parent.child
function setstatus!(parent::Node, isgc::Dict)
if isgc[parent.data.ip]
parent.data.status = 1
end
for c in parent
if isgc[c.data.ip] && (pisjl || !C)
parent.data.status = 1 # marks parent as triggering garbage collection
end
if !C
newc = prunegraph!(c, C, isjl, isgc)
if newc != parent
# newc is a valid child
if firstchild
parent.child = newc
firstchild = false
setstatus!(c, isgc)
end
end

# The last three inputs are just for debugging
function prunegraph!(parent::Node, isjl::Dict, lidict, ip2so, counts)
if parent.data.ip != 0
counts[ip2so[parent.data.ip]] += 1
end
c = parent.child
if parent == c
return
end
parent.child = parent # mark as a leaf unless we keep some children
lastc = c
isfirst = true
while true
prunegraph!(c, isjl, lidict, ip2so, counts)
if !isjl[c.data.ip]
parent.data.status |= c.data.status
if !isleaf(c)
# It has Julia children, splice them in
if isfirst
parent.child = c.child
isfirst = false
else
prevchild.sibling = newc
lastc.sibling = c.child
end
# one child might have become several children
prevchild = (newc == c) ? newc : lastsibling(newc)
elseif firstchild
# mark it tentatively as a leaf so it can be clipped
parent.child = parent
lastc = lastsibling(c.child)
end
end
end
if !C && !pisjl
if isleaf(parent)
# Prune entirely
parent.parent.data.status |= parent.data.status
return parent.parent
else
return parent.child
if isfirst
parent.child = c
else
lastc.sibling = c
end
isfirst = false
lastc = c
end
if c.sibling == c
lastc.sibling = lastc.sibling
break
end
c = c.sibling
end
parent
end


end
25 changes: 14 additions & 11 deletions src/tree.jl
Expand Up @@ -9,7 +9,7 @@ export Node,
isleaf,
lastsibling

# Tree representation:
# "Left-child, right-sibling" tree representation:
# Suppose a particular node, "A", has 3 children, "a", "b", and "c".
# "a", "b", and "c" link to "A" as their parent.
# "A" links "a" as its child; "a" links "b" as its sibling, and "b" links "c" as its sibling.
Expand Down Expand Up @@ -85,16 +85,19 @@ done(n::Node, state::Node) = n == state
next(n::Node, state::Node) = state, state == state.sibling ? n : state.sibling

function showedges(io::IO, parent::Node, printfunc = identity)
if isleaf(parent)
println(io, printfunc(parent.data), " has no children")
else
print(io, printfunc(parent.data), " has the following children: ")
for c in parent
print(io, printfunc(c.data), " ")
end
print(io, "\n")
for c in parent
showedges(io, c, printfunc)
str = printfunc(parent.data)
if str != nothing
if isleaf(parent)
println(io, str, " has no children")
else
print(io, str, " has the following children: ")
for c in parent
print(io, printfunc(c.data), " ")
end
print(io, "\n")
for c in parent
showedges(io, c, printfunc)
end
end
end
end
Expand Down
9 changes: 6 additions & 3 deletions test/pvtree.jl
Expand Up @@ -31,7 +31,8 @@ end

# Run it with C == false
root = buildraw()
PVTree.prunegraph!(root, false, isjl, isgc)
PVTree.setstatus!(root, isgc)
PVTree.prunegraph!(root, isjl)

@assert root.data.status == 1
c = root.child
Expand All @@ -50,13 +51,14 @@ c = c.sibling

# Now do it again, with C == true
root = buildraw()
PVTree.prunegraph!(root, true, isjl, isgc)
PVTree.setstatus!(root, isgc)
# PVTree.prunegraph!(root, isjl)

@assert root.data.status == 0
c = root.child
c1 = c
@assert c.data.ip == 1
@assert c.data.status == 1
@assert c.data.status == 0
c = c.sibling
@assert c.data.ip == 2
@assert c.data.status == 0
Expand All @@ -77,5 +79,6 @@ c = c.sibling
@assert c == c.sibling
c = c1.child
@assert c.data.ip == 7
@assert c.data.status == 1
@assert Tree.isleaf(c)
@assert c == c.sibling

0 comments on commit 42314a2

Please sign in to comment.