-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathtimer.py
157 lines (131 loc) · 5.87 KB
/
timer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import logging
import time
import numpy as np
import matplotlib.pyplot as plt
from engineering_notation import EngNumber as eng # only from pip
import atexit
LOGGING_LEVEL = logging.INFO
class CustomFormatter(logging.Formatter):
"""Logging Formatter to add colors and count warning / errors"""
grey = "\x1b[38;21m"
yellow = "\x1b[33;21m"
red = "\x1b[31;21m"
bold_red = "\x1b[31;1m"
reset = "\x1b[0m"
format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)"
FORMATS = {
logging.DEBUG: grey + format + reset,
logging.INFO: grey + format + reset,
logging.WARNING: yellow + format + reset,
logging.ERROR: red + format + reset,
logging.CRITICAL: bold_red + format + reset
}
def format(self, record):
log_fmt = self.FORMATS.get(record.levelno)
formatter = logging.Formatter(log_fmt)
return formatter.format(record)
def my_logger(name):
# logging.basicConfig(stream=sys.stdout, level=logging.INFO)
logger = logging.getLogger(name)
logger.setLevel(LOGGING_LEVEL)
# create console handler
ch = logging.StreamHandler()
ch.setFormatter(CustomFormatter())
logger.addHandler(ch)
return logger
log = my_logger(__name__)
timers = {}
times = {}
class Timer:
def __init__(self, timer_name='', delay=None,
show_hist=False, numpy_file=None):
""" Make a Timer() in a _with_ statement for a block of code.
The timer is started when the block is entered and stopped when exited.
The Timer _must_ be used in a with statement.
:param timer_name: the str by which this timer is repeatedly called
and which it is named when summary is printed on exit
:param delay: set this to a value to simply accumulate
this externally determined interval
:param show_hist: whether to plot a histogram with pyplot
:param numpy_file: optional numpy file path
"""
self.timer_name = timer_name
self.show_hist = show_hist
self.numpy_file = numpy_file
self.delay = delay
if self.timer_name not in timers.keys():
timers[self.timer_name] = self
if self.timer_name not in times.keys():
times[self.timer_name] = []
def __enter__(self):
if self.delay is None:
self.start = time.time()
return self
def __exit__(self, *args):
if self.delay is None:
self.end = time.time()
self.interval = self.end - self.start # measured in seconds
else:
self.interval = self.delay
times[self.timer_name].append(self.interval)
def print_timing_info(self, logger=None):
""" Prints the timing information accumulated for this Timer
:param logger: write to the supplied logger,
otherwise use the built-in logger
"""
if len(times) == 0:
log.error(f'Timer {self.timer_name} has no statistics; was it used without a "with" statement?')
return
a = np.array(times[self.timer_name])
timing_mean = np.mean(a) # todo use built in print method for timer
timing_std = np.std(a)
timing_median = np.median(a)
timing_min = np.min(a)
timing_max = np.max(a)
s='{} n={}: {}s +/- {}s (median {}s, min {}s max {}s)'.format(self.timer_name, len(a),
eng(timing_mean), eng(timing_std),
eng(timing_median), eng(timing_min),
eng(timing_max))
if logger is not None:
logger.info(s)
else:
log.info(s)
def print_timing_info():
for k, v in times.items(): # k is the name, v is the list of times
a = np.array(v)
timing_mean = np.mean(a)
timing_std = np.std(a)
timing_median = np.median(a)
timing_min = np.min(a)
timing_max = np.max(a)
log.info('== Timing statistics from all Timer ==\n{} n={}: {}s +/- {}s (median {}s, min {}s max {}s)'.format(k, len(a),
eng(timing_mean), eng(timing_std),
eng(timing_median), eng(timing_min),
eng(timing_max)))
if timers[k].numpy_file is not None:
try:
log.info(f'saving timing data for {k} in numpy file {timers[k].numpy_file}')
log.info('there are {} times'.format(len(a)))
np.save(timers[k].numpy_file, a)
except Exception as e:
log.error(f'could not save numpy file {timers[k].numpy_file}; caught {e}')
if timers[k].show_hist:
def plot_loghist(x, bins):
hist, bins = np.histogram(x, bins=bins) # histogram x linearly
if len(bins)<2 or bins[0]<=0:
log.error(f'cannot plot histogram since bins={bins}')
return
logbins = np.logspace(np.log10(bins[0]), np.log10(bins[-1]), len(bins)) # use resulting bin ends to get log bins
plt.hist(x, bins=logbins) # now again histogram x, but with the log-spaced bins, and plot this histogram
plt.xscale('log')
dt = np.clip(a,1e-6, None)
# logbins = np.logspace(np.log10(bins[0]), np.log10(bins[-1]), len(bins))
try:
plot_loghist(dt,bins=100)
plt.xlabel('interval[ms]')
plt.ylabel('frequency')
plt.title(k)
plt.show()
except Exception as e:
log.error(f'could not plot histogram: got {e}')
atexit.register(print_timing_info)