From ecd663b7e927f2ca306749eeee69242a131b44ec Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Wed, 26 Jan 2022 21:23:39 -0800 Subject: [PATCH] Some improvements to ratemeter. threading.lock to ensure consistency time.monotonic instead of time.time Docstring wrapping Remove math.ceil and round, not sure why I did that --- voussoirkit/ratemeter.py | 43 +++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/voussoirkit/ratemeter.py b/voussoirkit/ratemeter.py index 4b4b58d..5abbd3c 100644 --- a/voussoirkit/ratemeter.py +++ b/voussoirkit/ratemeter.py @@ -1,5 +1,5 @@ import collections -import math +import threading import time class RateMeter: @@ -9,17 +9,18 @@ class RateMeter: units per second over `span` seconds. Set `span` to None to calculate unit/s over the lifetime of the object - after the first digest, rather than over a span. - This saves the effort of tracking timestamps. Don't just use a large number! + after the first digest, rather than over a span. This saves the effort + of tracking timestamps; so don't just use a large number! ''' self.sum = 0 self.span = span + self.lock = threading.Lock() self.tracking = collections.deque() self.first_digest = None - def digest(self, value): - now = time.time() + def _digest(self, value): + now = time.monotonic() self.sum += value if self.span is None: @@ -27,8 +28,8 @@ class RateMeter: self.first_digest = now return - earlier = now - self.span - while len(self.tracking) > 0 and self.tracking[0][0] < earlier: + expire_cutoff = now - self.span + while len(self.tracking) > 0 and self.tracking[0][0] < expire_cutoff: (timestamp, pop_value) = self.tracking.popleft() self.sum -= pop_value @@ -37,19 +38,16 @@ class RateMeter: else: self.tracking[-1][1] += value - def report(self): - ''' - Return a tuple containing the running sum, the time span - over which the rate is being calculated, and the rate in - units per second. + def digest(self, value): + with self.lock: + return self._digest(value) - (sum, time_interval, rate) - ''' + def _report(self): # Flush the old values, ensure self.first_digest exists. - self.digest(0) + self._digest(0) if self.span is None: - now = math.ceil(time.time()) + now = time.monotonic() time_interval = now - self.first_digest else: # No risk of IndexError because the digest(0) ensures we have @@ -58,7 +56,16 @@ class RateMeter: if time_interval == 0: return (self.sum, 0, self.sum) + rate = self.sum / time_interval - time_interval = round(time_interval, 3) - rate = round(rate, 3) return (self.sum, time_interval, rate) + + def report(self): + ''' + Return a tuple containing the running sum, the time span over which the + rate has been calculated, and the rate in units per second. + + (sum, time_interval, rate) + ''' + with self.lock: + return self._report()