Skip to content

Commit

Permalink
Merge pull request #21 from ianlini/MultiStopwatch
Browse files Browse the repository at this point in the history
implement MultiStopwatch (resolved #18)
ianlini authored Apr 25, 2019
2 parents aba3f70 + edf6dbd commit faf70b2
Showing 9 changed files with 185 additions and 6 deletions.
1 change: 1 addition & 0 deletions bistiming/__init__.py
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
__version__ = pkg_resources.get_distribution("bistiming").version

from .stopwatch import Stopwatch # noqa: F401
from .multistopwatch import MultiStopwatch # noqa: F401


SimpleTimer = Stopwatch # backward-compatible to < 0.2
72 changes: 72 additions & 0 deletions bistiming/multistopwatch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from __future__ import print_function, division, absolute_import, unicode_literals
from six.moves import UserList
import datetime

from six.moves import range

from . import Stopwatch
from .utils import div_timedelta


class MultiStopwatch(UserList):
def __init__(self, iterable, *args, **kwargs):
if isinstance(iterable, int):
n = iterable
super(MultiStopwatch, self).__init__(Stopwatch(*args, **kwargs) for i in range(n))
else:
assert not args and not kwargs
super(MultiStopwatch, self).__init__(iterable)

def get_cumulative_elapsed_time(self):
"""Get the cumulative elapsed time of each stopwatch (including the current split).
Returns
-------
cumulative_elapsed_time : List[datetime.timedelta]
"""
return [stopwatch.get_cumulative_elapsed_time() for stopwatch in self]

def get_percentage(self):
"""Get the cumulative time percentage of each stopwatch (including the current split).
Returns
-------
cumulative_elapsed_time_percentage : List[float]
"""
cumulative_elapsed_time = self.get_cumulative_elapsed_time()
sum_elapsed_time = sum(cumulative_elapsed_time, datetime.timedelta())
return [div_timedelta(t, sum_elapsed_time) for t in cumulative_elapsed_time]

def get_n_splits(self):
"""Get number of splits of each stopwatch (excluding the current split).
Returns
-------
n_splits : List[int]
"""
return [len(stopwatch.split_elapsed_time) for stopwatch in self]

def get_mean_per_split(self):
"""Get the mean elapsed time per split of each stopwatch (excluding the current split).
Returns
-------
mean_elapsed_time_per_split : List[datetime.timedelta]
"""
return [div_timedelta(sum(stopwatch.split_elapsed_time, datetime.timedelta()),
len(stopwatch.split_elapsed_time))
for stopwatch in self]

def get_statistics(self):
"""Get all statistics as a dictionary.
Returns
-------
statistics : Dict[str, List]
"""
return {
'cumulative_elapsed_time': self.get_cumulative_elapsed_time(),
'percentage': self.get_percentage(),
'n_splits': self.get_n_splits(),
'mean_per_split': self.get_mean_per_split(),
}
1 change: 1 addition & 0 deletions bistiming/stopwatch.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from __future__ import print_function, division, absolute_import, unicode_literals
from functools import partial
import logging
import datetime
45 changes: 45 additions & 0 deletions bistiming/tests/test_multistopwatch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from __future__ import print_function, division, absolute_import, unicode_literals
from time import sleep
import unittest

from six.moves import range, zip
from examples import multistopwatch_examples
from bistiming import MultiStopwatch

from .utils import assert_timedelta_close_seconds_list, isclose


class TestMultiStopwatch(unittest.TestCase):
def test_multistopwatch_examples(self):
multistopwatch_examples.main()

def test_muulti_stopwatch(self):
timers = MultiStopwatch(2, verbose=False)
for i in range(5):
for i in range(2):
with timers[0]:
sleep(0.1)
with timers[1]:
sleep(0.1)

# test get_cumulative_elapsed_time()
assert_timedelta_close_seconds_list(
timers.get_cumulative_elapsed_time(),
[1, 0.5])

# test get_percentage()
for p, exp in zip(timers.get_percentage(), [2 / 3, 1 / 3]):
self.assertTrue(isclose(p, exp, rel_tol=0.05))

# test get_n_splits()
self.assertListEqual(timers.get_n_splits(), [10, 5])

# test get_mean_per_split()
assert_timedelta_close_seconds_list(
timers.get_mean_per_split(),
[0.1, 0.1])

# test get_statistics()
self.assertListEqual(
sorted(timers.get_statistics().keys()),
['cumulative_elapsed_time', 'mean_per_split', 'n_splits', 'percentage'])
9 changes: 3 additions & 6 deletions bistiming/tests/test_stopwatch.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
from __future__ import print_function, division, absolute_import, unicode_literals
import datetime
from time import sleep

from examples import stopwatch_examples
from bistiming import Stopwatch

from .utils import assert_timedelta_close_seconds


def test_stopwatch_examples():
stopwatch_examples.main()


def assert_timedelta_close_seconds(d, s, epsilon=0.05):
d_s = d.days * 86400 + d.seconds + d.microseconds * 0.000001
percentage_error = abs(d_s - s) / s
assert percentage_error < epsilon


def test_low_level_api():
timer = Stopwatch()
sleep(0.1)
17 changes: 17 additions & 0 deletions bistiming/tests/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from __future__ import print_function, division, absolute_import, unicode_literals
from six.moves import zip


def assert_timedelta_close_seconds_list(d_list, s_list, epsilon=0.05):
for d, s in zip(d_list, s_list):
assert_timedelta_close_seconds(d, s, epsilon)


def assert_timedelta_close_seconds(d, s, epsilon=0.05):
d_s = d.days * 86400 + d.seconds + d.microseconds * 0.000001
percentage_error = abs(d_s - s) / s
assert percentage_error < epsilon


def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
15 changes: 15 additions & 0 deletions bistiming/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from __future__ import print_function, division, absolute_import, unicode_literals
import datetime


def div_timedelta_int(d, i):
d_us = d.microseconds + 1000000 * (d.seconds + 86400 * d.days)
return datetime.timedelta(microseconds=d_us / i)


def div_timedelta(d1, d2):
if isinstance(d2, int):
return div_timedelta_int(d1, d2)
d1_us = d1.microseconds + 1000000 * (d1.seconds + 86400 * d1.days)
d2_us = d2.microseconds + 1000000 * (d2.seconds + 86400 * d2.days)
return d1_us / d2_us
30 changes: 30 additions & 0 deletions examples/multistopwatch_examples.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from __future__ import print_function, division, absolute_import, unicode_literals
from time import sleep
import pprint

from six.moves import range
from bistiming import MultiStopwatch


def basic_example():
timers = MultiStopwatch(2, verbose=False)
for i in range(5):
for i in range(2):
with timers[0]:
sleep(0.1)
with timers[1]:
sleep(0.1)
print("get_cumulative_elapsed_time():", timers.get_cumulative_elapsed_time())
print("get_n_splits():", timers.get_n_splits())
print("get_mean_per_split():", timers.get_mean_per_split())
print("get_percentage():", timers.get_percentage())
print("get_statistics():")
pprint.pprint(timers.get_statistics())


def main():
basic_example()


if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions examples/stopwatch_examples.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from __future__ import print_function, division, absolute_import, unicode_literals
from time import sleep
import logging

0 comments on commit faf70b2

Please sign in to comment.