|
"""Simple tools for timing functions' execution, when IPython is not available. """ |
|
|
|
|
|
import timeit |
|
import math |
|
|
|
|
|
_scales = [1e0, 1e3, 1e6, 1e9] |
|
_units = ['s', 'ms', '\N{GREEK SMALL LETTER MU}s', 'ns'] |
|
|
|
|
|
def timed(func, setup="pass", limit=None): |
|
"""Adaptively measure execution time of a function. """ |
|
timer = timeit.Timer(func, setup=setup) |
|
repeat, number = 3, 1 |
|
|
|
for i in range(1, 10): |
|
if timer.timeit(number) >= 0.2: |
|
break |
|
elif limit is not None and number >= limit: |
|
break |
|
else: |
|
number *= 10 |
|
|
|
time = min(timer.repeat(repeat, number)) / number |
|
|
|
if time > 0.0: |
|
order = min(-int(math.floor(math.log10(time)) // 3), 3) |
|
else: |
|
order = 3 |
|
|
|
return (number, time, time*_scales[order], _units[order]) |
|
|
|
|
|
|
|
|
|
def __do_timings(): |
|
import os |
|
res = os.getenv('SYMPY_TIMINGS', '') |
|
res = [x.strip() for x in res.split(',')] |
|
return set(res) |
|
|
|
_do_timings = __do_timings() |
|
_timestack = None |
|
|
|
|
|
def _print_timestack(stack, level=1): |
|
print('-'*level, '%.2f %s%s' % (stack[2], stack[0], stack[3])) |
|
for s in stack[1]: |
|
_print_timestack(s, level + 1) |
|
|
|
|
|
def timethis(name): |
|
def decorator(func): |
|
if name not in _do_timings: |
|
return func |
|
|
|
def wrapper(*args, **kwargs): |
|
from time import time |
|
global _timestack |
|
oldtimestack = _timestack |
|
_timestack = [func.func_name, [], 0, args] |
|
t1 = time() |
|
r = func(*args, **kwargs) |
|
t2 = time() |
|
_timestack[2] = t2 - t1 |
|
if oldtimestack is not None: |
|
oldtimestack[1].append(_timestack) |
|
_timestack = oldtimestack |
|
else: |
|
_print_timestack(_timestack) |
|
_timestack = None |
|
return r |
|
return wrapper |
|
return decorator |
|
|