From 4691a1efdcd8cbe4bc66d586307daf6505f21db4 Mon Sep 17 00:00:00 2001 From: Pavel Raiskup Date: Mon, 18 Jan 2016 19:05:25 +0100 Subject: [PATCH 2/2] make the progress bar more stable Add new sma_delta parameter. That parameter represents time delta (in seconds) for which _one item_ in deque() is valid until we create new one. Practically, one item represents number of items processed during 'sma_delta' time (by default 0.3s). Taking into account sma_delta and sma_window values, progress bar now returns statistics for the last (sma_delta*sma_window) seconds, which is by default 3s. Document a bit. This resolves verigak/progress/pull/24. --- progress/__init__.py | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/progress/__init__.py b/progress/__init__.py index 3130fe1..37b4547 100644 --- a/progress/__init__.py +++ b/progress/__init__.py @@ -24,9 +24,35 @@ from time import time __version__ = '1.2' +""" +The moving average is calculated by this formula: + + _in_window / (time() - oldest_timestamp_in_window) + +Because the frequency of calling next() callback might be rather very high, +doing the naive next()/avg() implementation leads to issues (PR 23): + + def next(N): + t = time() + delta = N / (t - last_timestamp) # very small number + _dt.append(delta) + _avg = sum(_dt)/len(_dt) + last_timestamp = t + +.. Even if 'len(_dt)' was 1000, the time frame can be very small (e.g. file +download for next(N=8k in bytes) on 1Gbit network. With the mentioned formula +the window size is limited by *time*, not by the next() callback frequency. + +The moving average is calculated -at most- for the last 3 seconds by default: + + sma_window x sma_delta = 10 x 0.3 = 3s + +Users can change this default window size, when needed. +""" class Infinite(object): file = stderr - sma_window = 10 + sma_window = 10 # Size of the window -- (max) number of sma_delta items. + sma_delta = 0.3 # Time-length of one item in window, in seconds. def __init__(self, *args, **kwargs): self.index = 0 @@ -67,7 +93,20 @@ class Infinite(object): def next(self, n=1): self._ts = time() - self._dt.append({'t': self._ts, 'n': n}) + + item = {'t': self._ts, 'n': 0} + if len(self._dt): + old_item = self._dt.pop() + if self._ts > old_item['t'] + self.sma_delta: + # Already reached timeout, we are not going to + # touch this item. Return it back. + self._dt.append(old_item) + else: + item = old_item + + item['n'] = item['n'] + n + + self._dt.append(item) self._in_window = self._in_window + n if len(self._dt) > self.sma_window: -- 2.7.4