-
Notifications
You must be signed in to change notification settings - Fork 0
/
stopwatch.py
362 lines (277 loc) · 9.26 KB
/
stopwatch.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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# pylint: disable=C0103, too-few-public-methods, locally-disabled, no-self-use, unused-argument, unused-import
"""Class with a stopwatch to calculate
the time and rates of events
This duplicates the one in opencvlite, but doesnt use the cv2 implementation
to get time.
"""
from time import sleep #for convieience
import datetime as _datetime
from collections import deque as _deque
import time as _time
from funclite.numericslib import round_normal as _rndnorm
def _clock():
"""
returns absolute time in seconds since
ticker started (usually when OS started
"""
# _cv2.getTickCount() / _cv2.getTickFrequency()
return _time.time()
class _StopWatchInterval():
"""stop watch interval"""
SMOOTH_COEFF = 0.5
def __init__(self, event_ticks=1, previous_interval=None, event_name=''):
"""(str, Class:StopWatchInterval)
"""
self.event_name = event_name
self.time = _clock()
self.event_ticks = event_ticks #event could be some thing
if previous_interval is None:
self.running_event_ticks = self.event_ticks
else:
self.running_event_ticks = previous_interval.running_event_ticks + self.event_ticks
self.previous_interval = previous_interval
@property
def event_rate(self):
"""Event rate in events/ms between
this interval and the previous one"""
if self.previous_interval is None:
return 0
return (self.time - self.previous_interval.time)/self.event_ticks
@property
def laptime(self):
"""Time difference in ms between
this interval and the previous one"""
try:
tm = self.time - self.previous_interval.time
except Exception as _:
tm = 0
return tm
@property
def event_rate_smoothed(self):
"""event laptime getter, as seconds"""
if self.previous_interval is None:
return self.event_rate
return _StopWatchInterval.SMOOTH_COEFF * self.previous_interval.event_rate + (1.0 - _StopWatchInterval.SMOOTH_COEFF) * self.event_rate
def __repr__(self):
s = 'Event %s took %.2f s, at a rate of %.2f [%.2f] per s' % (self.event_name, self.laptime, self.event_rate, self.event_rate_smoothed)
return s
class StopWatch():
"""
Provides access to timed metrics using a
stopwatch like operation and access to a
queue of lapped times. All times are seconds.
Provides smoothed estimates.
Doesn't start until lap is used.
lap:snapshot the time
Example:
>>>sw=stopwatch.StopWatch()
>>>print(sw.event_rate)
0
>>>sw.lap()
>>>print(sw.event_rate)
0.0123
"""
def __init__(self, qsize=5, event_name=''):
self.qsize = qsize
self._event_name = event_name
self._prevInterval = None
self.reset()
def reset(self):
"""reset the stopwatch,
Clears the queue, and
sets birth_time to current system ticks
"""
self.Times = _deque(maxlen=self.qsize)
self._birth_time = _clock()
self._firstInterval = _StopWatchInterval(0, None, self._event_name)
self._prevInterval = self._firstInterval
self.Times.append(self._firstInterval)
def remaining(self, n: int, use_smoothed: bool = True) -> float:
"""
Estimate time left in seconds using the smoothed event time
Args:
n (int): number of events left
use_smoothed (bool): use the smoothed event, otherwise absolute
Returns:
float: time left in seconds
Notes:
don't use smoothed till queue is full
Examples:
>>>remaining(10, use_smoothed=True)
23.12123
"""
try:
x = n * self.event_rate_smoothed if use_smoothed else n * self.event_rate
except Exception as _:
try:
x = n * self.event_rate_global
except Exception as _:
x = 0
return x
def remaining_global(self, n: int) -> float:
"""
Estimate time left in seconds using the global mean event time
Args:
n (int): number of events left
Returns:
float, time left in seconds
Examples:
>>> remaining_global(10) # noqa
23.12123
"""
try:
x = n * self.event_rate_global
except Exception as _:
x = 0
return x
def pretty_remaining(self, n, use_smoothed=True):
"""(int, bool) -> str
Return pretty formatted string of the time
remaining
Parameters:
n: number of events left
use_smoothed: use the smoothed event rates, rather than unsmoothed
Returns:
pretty string with an estimate of the time remaining
Examples:
>>> SW.pretty_remaining(10, use_smoothed=True) # noqa
4d 2h 23m
"""
return StopWatch.pretty_time(self.remaining(n, use_smoothed))
def pretty_remaining_global(self, n):
"""(int) -> str
Return pretty formatted string of the time
remaining, using the global mean
Args:
n (int): number of events left
Returns:
pretty string with an estimate of the time remaining
Examples:
>>> SW.pretty_remaining_global(10) # noqa
4d 2h 23m
"""
return StopWatch.pretty_time(self.remaining_global(n))
def lap(self, event_ticks: int = 1) -> None:
"""
Add a 'lap' time to the queue
Args:
event_ticks (int): number of events since the last lap. Used to calculate event rates/ms
Returns: None
"""
Int = _StopWatchInterval(event_ticks, self._prevInterval, self._event_name)
self.Times.append(Int)
self._prevInterval = Int
@property
def run_time(self) -> int:
""" -> float
Returns:
int: Time elapsed in seconds since timer initialised or reset
"""
return _clock() - self._birth_time
@property
def birth_time(self):
"""birth_time getter"""
return self._birth_time
@property
def event_rate(self):
""" -> float
Get the mean event rate in seconds using
the StopWatchInterval Queue.
Returns:
float of the mean event rate
Example:
>>>SW.event_rate
2.1212441
"""
Is = list(self.Times)
if Is:
ts = [v.event_rate for v in Is]
if ts:
return sum(ts) / len(ts)
return None
return None
@property
def event_rate_smoothed(self):
""" -> float
Get the mean smoothed event rate in seconds using
the StopWatchInterval Queue.
Returns:
float of the mean event rate
Example:
>>>SW.event_rate_smoothed
2.1212441
"""
Is = list(self.Times)
if Is:
ts = [v.event_rate_smoothed for v in Is]
if ts:
return sum(ts) / len(ts)
return None
return None
@property
def event_rate_global(self):
"""(void) -> float
Get rate over the
lifetime of the StopWatch instance.
"""
t = self.Times[-1]
assert isinstance(t, _StopWatchInterval)
return (t.time - self._birth_time)/t.running_event_ticks
@property
def event_rate_last(self):
"""(void) -> float
Get the latest "raw" rate.
"""
t = self.Times[-1]
assert isinstance(t, _StopWatchInterval)
return t.event_rate
@property
def event_rate_last_smoothed(self):
"""(void) -> float
Get the latest smoothed rate.
"""
t = self.Times[-1]
assert isinstance(t, _StopWatchInterval)
return t.event_rate_smoothed
@property
def laptime(self):
"""(void) -> float
Get last "laptime".
"""
return self.Times[-1].laptime
def __repr__(self):
if self.Times:
evt = 'lap' if self._event_name == '' else self._event_name
s = str(self.Times[-1])
s = 'Birth time %s. Last %s :%s' % (self._birth_time, evt, s)
else:
s = 'Birth time %s. No laps.' % self._birth_time
return s
@staticmethod
def pretty_now():
"""() -> str
Pretty date time"""
return _time.strftime("%Y-%m-%d %H:%M")
@staticmethod
def pretty_time(secs):
"""pretty print a duration"""
return _time_pretty(secs)
def _time_pretty(seconds):
"""(float) -> str
Return a prettified time interval
for printing
"""
sign_string = '-' if seconds < 0 else ''
if seconds < 10:
return format(seconds, '.2f') + 's'
seconds = abs(_rndnorm(seconds))
days, seconds = divmod(seconds, 86400)
hours, seconds = divmod(seconds, 3600)
minutes, seconds = divmod(seconds, 60)
if days > 0:
return '%s%dd %dh %dm %ds' % (sign_string, days, hours, minutes, seconds)
if hours > 0:
return '%s%dh %dm %ds' % (sign_string, hours, minutes, seconds)
if minutes > 0:
return '%s%dm %ds' % (sign_string, minutes, seconds)
return '%s%ds' % (sign_string, seconds)