import time, os, collections, statistics, logging, numpy
log = logging.getLogger(__name__)
_summary_types = dict(
sum=sum,
mean=statistics.mean,
min=min,
max=max,
median=statistics.median,
)
class _TimerGetter:
def __init__(self, timer, summary):
self.timer = timer
self.summary = summary
def __getattr__(self, name):
if name in ('timer', 'checkpoints'):
raise AttributeError
if name in self.timer.checkpoints:
return self.summary(self.timer.checkpoints[name])
raise AttributeError("Timer has no attribute named: " + name)
[docs]class Timer:
def __init__(self, name='Timer', verbose=False):
self.name = name
self.verbose = verbose
self.sum = _TimerGetter(self, numpy.sum)
self.mean = _TimerGetter(self, numpy.mean)
self.min = _TimerGetter(self, numpy.min)
self.max = _TimerGetter(self, numpy.max)
self.median = _TimerGetter(self, numpy.median)
[docs] def start(self):
return self.__enter__()
[docs] def stop(self):
return self.__exit__()
def __enter__(self):
if self.verbose: log.debug(f'{self.name} intialized')
self.start = time.perf_counter()
self.last = self.start
self.checkpoints = collections.defaultdict(list)
return self
[docs] def checkpoint(self, name='none', verbose=False):
t = time.perf_counter()
self.checkpoints[name].append(t - self.last)
self.last = t
if self.verbose or verbose:
log.debug(f'{self.name} checkpoint {name} iter {len(self.checkpoints[name])}' +
f'time {self.checkpoints[name][-1]}')
return self
def __exit__(self, type=None, value=None, traceback=None):
self.checkpoints['total'].append(time.perf_counter() - self.start)
if self.verbose: log.debug(f'{self.name} finished')
if self.verbose: self.report()
return self
def __getattr__(self, name):
if name == "checkpoints":
raise AttributeError
if name in self.checkpoints:
return self.checkpoints[name]
raise AttributeError("Timer has no attribute named: " + name)
[docs] def alltimes(self, name):
return self.checkpoints[name]
[docs] def report_dict(self, order='longest', summary='sum'):
if not callable(summary):
if summary not in _summary_types:
raise ValueError('unknown summary type: ' + str(summary))
summary = _summary_types[summary]
if order == 'longest':
reordered = sorted(self.checkpoints.items(), key=lambda kv: -summary(kv[1]))
return {k: summary(v) for k, v in reordered}
elif order == 'callorder':
return self.checkpoints
else:
raise ValueError('Timer, unknown order: ' + order)
[docs] def report(self, order='longest', summary='sum', namelen=None, precision='10.5f',
printme=True):
if namelen is None:
namelen = max(len(n) for n in self.checkpoints)
lines = [f"Times(order={order}, summary={summary}):"]
times = self.report_dict(order=order, summary=summary)
for cpoint, t in times.items():
lines.append(f' {cpoint:>{namelen}} {t:{precision}}')
r = os.linesep.join(lines)
if printme: log.info(r)
return r
@property
def total(self):
if 'total' in self.checkpoints:
return sum(self.checkpoints['total'])
return time.perf_counter() - self.start
def __str__(self):
return self.report(printme=False)
[docs] def merge(self, others):
if isinstance(others, Timer): others = [others]
for other in others:
for k, v in other.checkpoints.items():
self.checkpoints[k].extend(v)