Skip to content

Instantly share code, notes, and snippets.

@quinnkj
Created October 25, 2020 16:45
Show Gist options
  • Save quinnkj/c4bffd31adc25ccf4d6d442390be5811 to your computer and use it in GitHub Desktop.
Save quinnkj/c4bffd31adc25ccf4d6d442390be5811 to your computer and use it in GitHub Desktop.
Cookbook Recipes for timing blocks of code at runtime.
"""
webapp/core/utilities/stopwatch.py
Example Usage:
```
def countdown(n):
while n > 0:
n -= 1
# Use 1: Explicit start/stop
t = StopwatchTimer()
t.start()
countdown(1000000)
t.stop()
print(t.elapsed)
# Use 2: As a context manager
with t:
countdown(1000000)
print(t.elapsed)
with StopwatchTimer() as t2:
countdown(1000000)
print(t2.elapsed)
```
"""
import time
from contextlib import contextmanager
from typing import Any, Callable, Optional
from functools import wraps
class StopwatchTimer:
"""StopwatchTimer
This class defines a timer that can be started, stopped, and reset
as needed by the user. Keep track of the total elapsed time in
the elapsed attribute.
Info: the `time.perf_counter()` function always uses the
highest-resolution timer available on the system.
"""
def __init__(self, func: Callable[[], float] = time.perf_counter):
"""__init__
Args:
func (Callable[[], float]):
default=time.perf_counter
"""
self.elapsed: float = 0.0
self._func: Callable[[], float] = func
self._start: Optional[float] = None
def start(self):
"""start"""
if self._start is not None:
raise RuntimeError("Already started")
self._start = self._func()
def stop(self):
"""stop"""
if self._start is None:
raise RuntimeError("Not started")
end = self._func()
self.elapsed += end - self._start
self._start = None
def reset(self):
"""reset"""
self.elapsed = 0.0
@property
def running(self):
"""running"""
return self._start is not None
def __enter__(self):
"""__enter__"""
self.start()
return self
def __exit__(self, *args):
"""__exit__"""
self.stop()
@staticmethod
def time_function(method=time.process_time):
"""Time function decorator
- Select between either:
- `perf_counter`: wall-clock time
- or
- `process_time`: process time
Simple method decorator for the selected profiling of functions,
a short decorator can be useful.
Example Usage:
```
if __name__ == "__main__":
@StopwatchTimer.time_function()
def countdown(n):
while n > 0:
n -= 1
countdown(10000000)
```
"""
def decorate(func: Callable[[Any, ...], Any]) -> Any:
"""decorate
Args:
func (Callable[[Any, ...], Any]):
Returns:
(Any)
"""
# noinspection PyUnresolvedReferences
@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
"""wrapper
Args:
*args (Any):
**kwargs (Any):
Returns:
(Any)
"""
start = method()
ret = func(*args, **kwargs)
end = method()
print(f"{func.__module__}.{func.__name__} : {end - start}")
return ret
return wrapper
return decorate
@staticmethod
@contextmanager
def time_block(label: str, method: Callable[[], float] = time.perf_counter) -> None:
"""Time Block
Time the encapsulated block of code within the context manager.
Args:
label (str):
method (Callable[[], float]):
default=time.perf_counter
Returns:
(None)
Example Usage:
```
if __name__ == "__main__":
with StopwatchTimer.time_block("counting"):
n = 10000000
while n > 0:
n -= 1
```
"""
start = method()
try:
yield
finally:
end = method()
print(f"{label} : {end - start}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment