-
Notifications
You must be signed in to change notification settings - Fork 19
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
Add a Hierarchical timer class #96
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am eager for this functionality, but have concerns that it duplicates much of the functionality in the TicTocTimer but in a different way. Would you consider refactoring this to have the _HierarchicalHelper
use TicTocTimers? There are some features in the TicTocTimer that provide more robustness (notably, higher precision timers on Windows).
pyutilib/misc/timing.py
Outdated
parent.timers[identifier] = _HierarchicalHelper() | ||
return parent.timers[identifier] | ||
|
||
def start_increment(self, identifier): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TicTocTimer uses start()
and stop()
. Can we make the two APIs consistent?
pyutilib/misc/timing.py
Outdated
s += name_formatter.format(name=name) | ||
s += '{0:>15.2e}'.format(timer.total_time) | ||
s += '{0:>15d}'.format(timer.n_calls) | ||
s += '{0:>15.2e}\n'.format(timer.total_time/timer.n_calls) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can n_calls
ever be 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, n_calls can never be 0. The timer would just not exist in that case.
pyutilib/misc/timing.py
Outdated
stack = identifier.split('.') | ||
timer = self._get_timer_from_stack(stack) | ||
parent = self._get_timer_from_stack(stack[:-1]) | ||
return timer.total_time / parent.total_time * 100 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For timing very short times on machines with low precision, total_time
can be 0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fixed this in the printing methods. However, I think a division by 0 error is appropriate here. Other methods are available for users to easily get the numerator and denominator individually. Although, given what I just said, it might make sense to remove the percent methods. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I think that either get_relative_percent
should return 0
when total_time
is 0 so that clients that are doing the equivalent of '%d' % (t.get_relative_percent)
will not get exceptions. It is numerically incorrect, but doesn't meaningfully change the interpretation of the results.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another edge case: if you call get_relative_percent
from the top of the stack, you should get 100
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. That should now be fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree - I think returning 0
from get_relative_percent_time
when parent.total_time
is 0
does change the interpretation of the results. I would rather the user get an exception. However, I am happy to remove this method completely and let users do what they want manually (with calls to get_total_time
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, we could return math.nan
, which is accurate and does not raise an exception. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michaelbynum Great idea! Just make it float('nan')
(for compatibility).
Codecov Report
@@ Coverage Diff @@
## master #96 +/- ##
==========================================
+ Coverage 63.13% 63.93% +0.79%
==========================================
Files 87 87
Lines 8788 8916 +128
==========================================
+ Hits 5548 5700 +152
+ Misses 3240 3216 -24
Continue to review full report at Codecov.
|
@jsiirola Great suggestions. I never realized the |
pyutilib/misc/timing.py
Outdated
ab 4.84e-02 10 4.84e-03 42.0% | ||
other 8.00e-04 N/A N/A 0.7% | ||
b 1.39e-02 10 1.39e-03 10.8% | ||
other 3.49e-04 N/A N/A 0.3% |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you consider changing this format to more closely match the results from the Python profiler? For reference, the profiler generates:
Function called...
ncalls tottime cumtime
{posix.waitpid} ->
sre_compile.py:228(_compile_charset) -> 122 0.026 0.065 sre_compile.py:256(_optimize_charset)
502 0.000 0.000 {method 'append' of 'list' objects}
20 0.000 0.000 {method 'extend' of 'list' objects}
I am thinking about a format similar to:
Identifier ncalls tottime percall %
------------------------------------------------------------------------
all 1 0.129 0.129 100.0
--------------------------------------------------------------------
a 10 0.115 0.115 89.0
----------------------------------------------------------------
aa 50 0.066 0.001 57.3
ab 10 0.048 0.005 42.0
other n/a 0.001 n/a 0.7
================================================================
b 10 0.014 0.014 10.8
other n/a 0.000 n/a 0.3
====================================================================
========================================================================
...I am happy to draft the patch if you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this output way more than what I had. I honestly couldn't come up with anything I really liked, so I just gave up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can take a stab at it right now.
@jsiirola I think this is ready now. Thanks for all the great suggestions. I am very happy with this timer now. |
@michaelbynum: some questions/comments as I go through this:
I have some ideas around addressing the above. Would you like me to draft something as a PR to your branch? |
|
@jsiirola Looks good. Thanks for these changes. |
I agree. Thank you both for this - this looks great - and will definitely get used in a few of our projects. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine to merge. Remaining comments have been archived as #97.
Summary/Motivation:
This Pr adds a class for hierarchical timing.
Legal Acknowledgement
By contributing to this software project, I agree to the following terms and conditions for my contribution: