-
Notifications
You must be signed in to change notification settings - Fork 139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Showing time instead of cumulative in the icicle boxes #156
base: master
Are you sure you want to change the base?
Conversation
…mber in the boxes is actually proportional to length of the boxes.
Python 2.7 failures are unrelated to this commit. |
Thanks for making a PR to snakeviz! I agree it can be confusing how times don't always match box size when functions are called from multiple places, but it is my intent that box size reflects and displays cumulative time, because the graphic shows functions and the functions they call, which is what cumulative time is a measure of. |
Where actually is code that is computing the width of a box? In drawsvg.js, I see vis.data([json])...attr("width",function(d){return x(d.dx);}) but I can't figure out where that dx is computed. The metric used to compute d.dx should also be the one that is written in the box - everything else is confusing. You seem to suggest that that time is cumulative time but I think that is wrong: A box represents a function invocation, not a function, that is if you walk up the parenting boxes, you get the call stack and the width of the box should only be the time spent in the function when called through this particular chain. The good news is: you are actually computing that time correctly (I don't know how see above question). The bad news is: that time is not the cumulative time written into the box. |
Snakeviz does attempt to display the block width as time spent in that particular part of the call tree, but that is unfortunately not something cprofile actually records. When a function is called from multiple places in the call tree cprofile combines the metrics for those calls, leaving snakeviz without the info to know how much time was spent in the different places. That might be part of what you're seeing. It'd be really helpful if you could share a profile or code snippet that illustrates the problem you're seeing so I can help diagnose it. The code that builds the call tree is buried here: snakeviz/snakeviz/templates/viz.html Lines 172 to 246 in b8e9f9a
|
I came up with an example to illustrate my point:
Here is the image when the text in the box "cumulative" (as it is now): https://github.com/unhyperbolic/snakeviz/blob/bugImages/labeledByCumulative.png From looking at the images, I am confused by your comments: unlike you claim, cProfile does seem to give the necessary information to draw the boxes correctly. But writing cumulative as text into the boxes yields inconsistent results (e.g. one of the two invocation sof sumOfSquares is grouped under indirectSumOfSquares but its time is larger than its parent). Thanks for sending the code snippet. There is still a disconnect in my mind: drawIcicle is using "d.dx" for the width. But the code snipped you sent is not setting dx anywhere. Where is assignment of a value to dx happening? |
Just found the code that is computing the widths of the boxes: snakeviz/snakeviz/static/drawsvg.js Lines 39 to 41 in b8e9f9a
And here is the code that is putting the text in the boxes: snakeviz/snakeviz/static/drawsvg.js Lines 296 to 297 in b8e9f9a
I hope that makes the inconsistency crystal clear and why my commit is the right fix. |
I spent some time re-familiarizing myself with this code and now have a good grasp of what's on. As described in other issues, especially #112, the data made available by cProfile has some serious limitations when it comes to reconstructing a call tree with accurate timings in every part of the tree. The The Sometimes that guess is spot on and the size of the box is correct and the import time
def sleep8():
time.sleep(0.5)
sleep3()
def sleep3():
time.sleep(0.3)
def sleep1():
time.sleep(0.1)
def sleepn(n):
for _ in range(n):
sleep1()
def root():
sleep8()
sleep3()
sleepn(1)
root() Current snakeviz shows this profile: That shows how the parent time is propagated down to children when the child's cumulative time would exceed the parents', which in this case does result in the information displayed being more intuitive. However, the guess can also be totally wrong, especially in cases where the same parent/child call results in very different execution times in different parts of the call tree. This is well illustrated by the example code from #112: """https://github.com/jiffyclub/snakeviz/issues/112"""
import time
def a(t0, t1):
c(t0)
d(t1)
def b():
a(1, 4)
def c(t):
time.sleep(t)
def d(t):
time.sleep(t)
if __name__ == "__main__":
a(4, 1) # 5 seconds total, 4 in c, 1 in d
b() # 5 seconds total, 1 in c, 4 in d For that the current snakeviz shows this: And your change results in this: Those are both wrong, thanks to the limited information provided by cProfile. Given the impossibility of showing completely correct info I decided to show the cumulative time spent in a function as reported by cProfile. That way at least the value shown matches what one sees in the table below or when using the |
Thanks for your explanation. I see... That's unfortunate on cProfile's part :( |
There is another project pyprof2calltree, also see http://thomas-cokelaer.info/blog/2013/10/profiling-python-with-valgrind/. |
I don't know for sure, but it's working from the same limited cProfile source so I would expect so. |
I find it very confusing that the size of the boxes and the number of the boxes do not correlate. In particular, if a function has multiple callees, you can have a situation where a child box has a larger value than the parent box.
This fixes it.