Source code for radical.utils.timing
__author__ = "Radical.Utils Development Team"
__copyright__ = "Copyright 2014-2020, RADICAL@Rutgers"
__license__ = "MIT"
# pylint: disable=protected-access
import time
import threading as mt
from datetime import datetime
# ------------------------------------------------------------------------------
#
# static datetime instance used for the `epoch` helper method
_epoch = datetime(1970, 1, 1)
# ------------------------------------------------------------------------------
#
[docs]def epoch(data, pattern):
'''
convert a given datetime string into seconds since EPOCH.
The string is parsed as defined by POSIX's `strptime()`.
'''
return dt_epoch(datetime.strptime(data, pattern))
# ------------------------------------------------------------------------------
#
[docs]def dt_epoch(dt):
'''
convert a given datetime instance into seconds since EPOCH.
'''
return (dt - _epoch).total_seconds()
# ------------------------------------------------------------------------------
#
# Provides class decorators to time all public class methods
#
# The class decorator is provided as decorator method `@timed_class`, and as
# decorator class `@TimedClass`. It thus also serves as documentation on the
# different ways to implement class decorators.
#
# This is called on *decorator class* instantiation
[docs]def timed_method(func):
'''
This class decorator will decorate all public class methods with a timing
function. That will time the call invocation, and store the respective data
in a private class dict '__timing'. Additionally, it will add the class
method '_timing_last()', which will return the tuple `[method name, method
timer]` for the last timed invocation of a class method.
'''
# apply timing decorator to all public methods
def func_timer(obj, *args, **kwargs):
try:
_ = obj.__timing
assert _
except Exception:
# never seen this one before -- create private timing dict, and add
# timing_last method
obj.__timing = dict()
def timing_last():
last_call = obj.__timing.get('__last_call', None)
timer = obj.__timing.get(last_call, [None])[0]
return last_call, timer
def timing_stats():
import math
ret = ""
for key in sorted(obj.__timing.keys()):
if key == '__last_call':
continue
tlist = obj.__timing[key]
tnum = len(tlist)
tmean = sum(tlist) / tnum
tdev = [x - tmean for x in tlist]
tdev2 = [x * x for x in tdev]
tstd = math.sqrt(sum(tdev2) / tnum)
ret += "%-20s: %10.3fs (+/- %5.3fs) [n=%5d]\n" \
% (key, tmean, tstd, tnum)
return ret
def timing_reset():
obj.__timing = dict()
obj._timing_last = timing_last
obj._timing_stats = timing_stats
obj._timing_reset = timing_reset
# object is set up -- time the call and record timings
fname = func.__name__
tdict = obj.__timing
start = time.time()
ret = func(obj, *args, **kwargs)
stop = time.time()
timed = stop - start
if fname not in tdict:
tdict[fname] = list()
tdict[fname].append(timed)
tdict['__last_call'] = fname
return ret
return func_timer
# ------------------------------------------------------------------------------
#
[docs]class Time(object):
'''
This is a timing class that allows to simulate different types of clocks.
Parameters:
tick: This is the resolution of the clock.
speed: This is the speed of the clock.
'''
# --------------------------------------------------------------------------
#
def __init__(self, tick=0.01, speed=1.0):
self._tick = tick
self._speed = speed
self._seconds = 0.0
self._term = mt.Event()
self._lock = mt.Lock()
self._clock = mt.Thread(target=self._clock_thread)
self._clock.daemon = True
self._clock.start()
# --------------------------------------------------------------------------
#
[docs] def stop(self):
'''
Stops the clock
'''
self._term.set()
self._clock.join()
# --------------------------------------------------------------------------
#
def _clock_thread(self):
last = time.time()
while not self._term.is_set():
time.sleep(self._tick)
now = time.time()
with self._lock:
self._seconds += (now - last) * self._speed
last = now
# --------------------------------------------------------------------------
#
[docs] def time(self):
'''
Returns the current value of the clock
'''
return self._seconds
# --------------------------------------------------------------------------
#
[docs] def sleep(self, amount):
'''
Sleeps for a specific amount of time units. Actual time spent is equal
to amount divided by speed.
'''
time.sleep(amount / self._speed)
# --------------------------------------------------------------------------
#
[docs] def advance(self, amount):
'''
Advance the clock with a specific amount of time units
'''
with self._lock:
self._seconds += amount
# ------------------------------------------------------------------------------