|
|
22856fa |
Fix fast upload avg calculation
|
|
|
22856fa |
|
|
|
22856fa |
Upstream verigak/progress is broken for very fast uploads; e.g.
|
|
|
22856fa |
with 'copr build' command, the next() call is called so often that
|
|
|
22856fa |
the avg() calculation probably suffers from some small
|
|
|
22856fa |
floating-point numbers problems:
|
|
|
22856fa |
|
|
|
22856fa |
With 15MB/s => next() called for each 8096B => ~2000 calls/s
|
|
|
22856fa |
|
|
|
22856fa |
Since the upstream default window size is only of size 10 items
|
|
|
22856fa |
(by default), it calculates the average speed only for the last
|
|
|
22856fa |
~0.005s. We could enlarge the size of window (sma_window param),
|
|
|
22856fa |
but the algorithm is so naive that it would decrease the
|
|
|
22856fa |
performance.
|
|
|
22856fa |
|
|
|
22856fa |
This has been discussed very extensively with upstream (PR 24 and
|
|
|
22856fa |
friends) but I neither was not able to explain the problem, nor I
|
|
|
22856fa |
was able to convince upstream to accept my patches.
|
|
|
22856fa |
|
|
|
22856fa |
This downstream patch - while it keeps the backward API
|
|
|
22856fa |
compatibility - changes the algorithm so the average speed is
|
|
|
22856fa |
calculation is fast enough, and much more stable (by default it
|
|
|
22856fa |
calculates speed for window of 2 seconds).
|
|
|
22856fa |
|
|
|
e39309c |
We also don't seem to have the monotonic() mess, since we don't seem
|
|
|
e39309c |
to suffer from the same issues.
|
|
|
e39309c |
|
|
|
22856fa |
Fork with this patch backported is maintained in
|
|
|
22856fa |
https://github.com/python-progress/python-progress
|
|
|
22856fa |
|
|
|
e39309c |
diff --git a/MANIFEST.in b/MANIFEST.in
|
|
|
e39309c |
index ef7c4cb..0c73842 100644
|
|
|
e39309c |
--- a/MANIFEST.in
|
|
|
e39309c |
+++ b/MANIFEST.in
|
|
|
e39309c |
@@ -1,2 +1 @@
|
|
|
e39309c |
include README.rst LICENSE
|
|
|
e39309c |
-include test_*.py
|
|
|
22856fa |
diff --git a/progress/__init__.py b/progress/__init__.py
|
|
|
e39309c |
index e434c25..1cbdce6 100644
|
|
|
22856fa |
--- a/progress/__init__.py
|
|
|
22856fa |
+++ b/progress/__init__.py
|
|
|
e39309c |
@@ -18,10 +18,7 @@ from collections import deque
|
|
|
e39309c |
from datetime import timedelta
|
|
|
e39309c |
from math import ceil
|
|
|
e39309c |
from sys import stderr
|
|
|
e39309c |
-try:
|
|
|
e39309c |
- from time import monotonic
|
|
|
e39309c |
-except ImportError:
|
|
|
e39309c |
- from time import time as monotonic
|
|
|
e39309c |
+from time import time
|
|
|
e39309c |
|
|
|
e39309c |
|
|
|
e39309c |
__version__ = '1.5'
|
|
|
e39309c |
@@ -30,19 +27,55 @@ HIDE_CURSOR = '\x1b[?25l'
|
|
|
e39309c |
SHOW_CURSOR = '\x1b[?25h'
|
|
|
22856fa |
|
|
|
22856fa |
|
|
|
22856fa |
+class _Window(object):
|
|
|
22856fa |
+ max_seconds = 2
|
|
|
22856fa |
+ max_items = None
|
|
|
22856fa |
+
|
|
|
22856fa |
+ def __init__(self, max_seconds=2, max_items=None):
|
|
|
22856fa |
+ self.max_seconds = max_seconds
|
|
|
22856fa |
+ self.max_items = max_items
|
|
|
22856fa |
+
|
|
|
22856fa |
+ stamp = time()
|
|
|
22856fa |
+ self.last = stamp - 0.001
|
|
|
22856fa |
+ self.counter = 0
|
|
|
22856fa |
+ self.deque = deque()
|
|
|
22856fa |
+ self.next(0, stamp)
|
|
|
22856fa |
+
|
|
|
22856fa |
+ def pop(self):
|
|
|
22856fa |
+ item = self.deque.popleft()
|
|
|
22856fa |
+ self.counter -= item[1]
|
|
|
22856fa |
+
|
|
|
22856fa |
+ def clean(self):
|
|
|
22856fa |
+ if self.max_items:
|
|
|
22856fa |
+ while len(self.deque) > self.max_items:
|
|
|
22856fa |
+ self.pop()
|
|
|
22856fa |
+ while len(self.deque) > 2 and self.last - self.deque[0][0] > float(self.max_seconds):
|
|
|
22856fa |
+ self.pop()
|
|
|
22856fa |
+
|
|
|
22856fa |
+ def next(self, n, t):
|
|
|
22856fa |
+ self.clean()
|
|
|
22856fa |
+ self.deque.append((self.last, n))
|
|
|
22856fa |
+ self.last = t
|
|
|
22856fa |
+ self.counter += n
|
|
|
22856fa |
+
|
|
|
22856fa |
+ @property
|
|
|
22856fa |
+ def avg(self):
|
|
|
22856fa |
+ return self.counter / (self.last - self.deque[0][0])
|
|
|
22856fa |
+
|
|
|
22856fa |
+
|
|
|
22856fa |
class Infinite(object):
|
|
|
22856fa |
file = stderr
|
|
|
22856fa |
- sma_window = 10 # Simple Moving Average window
|
|
|
22856fa |
+ # Maximum number of next() calls to be held in Simple Moving Average
|
|
|
22856fa |
+ # window structure (in memory), default is unlimited.
|
|
|
22856fa |
+ sma_window_seconds = 2
|
|
|
22856fa |
+ sma_window = None
|
|
|
e39309c |
check_tty = True
|
|
|
e39309c |
hide_cursor = True
|
|
|
22856fa |
|
|
|
e39309c |
def __init__(self, message='', **kwargs):
|
|
|
22856fa |
self.index = 0
|
|
|
e39309c |
- self.start_ts = monotonic()
|
|
|
22856fa |
- self.avg = 0
|
|
|
e39309c |
- self._avg_update_ts = self.start_ts
|
|
|
22856fa |
- self._ts = self.start_ts
|
|
|
22856fa |
- self._xput = deque(maxlen=self.sma_window)
|
|
|
e39309c |
+ self.start_ts = time()
|
|
|
22856fa |
+ self.window = _Window(self.sma_window_seconds, self.sma_window)
|
|
|
22856fa |
for key, val in kwargs.items():
|
|
|
22856fa |
setattr(self, key, val)
|
|
|
22856fa |
|
|
|
e39309c |
@@ -62,23 +95,19 @@ class Infinite(object):
|
|
|
22856fa |
|
|
|
e39309c |
@property
|
|
|
e39309c |
def elapsed(self):
|
|
|
e39309c |
- return int(monotonic() - self.start_ts)
|
|
|
e39309c |
+ return int(time() - self.start_ts)
|
|
|
e39309c |
+
|
|
|
22856fa |
+ @property
|
|
|
22856fa |
+ def avg(self):
|
|
|
22856fa |
+ speed = self.window.avg
|
|
|
22856fa |
+ if speed:
|
|
|
22856fa |
+ return 1/speed
|
|
|
22856fa |
+ return 3600 # better constant?
|
|
|
e39309c |
|
|
|
22856fa |
@property
|
|
|
22856fa |
def elapsed_td(self):
|
|
|
22856fa |
return timedelta(seconds=self.elapsed)
|
|
|
22856fa |
|
|
|
22856fa |
- def update_avg(self, n, dt):
|
|
|
22856fa |
- if n > 0:
|
|
|
e39309c |
- xput_len = len(self._xput)
|
|
|
22856fa |
- self._xput.append(dt / n)
|
|
|
e39309c |
- now = monotonic()
|
|
|
e39309c |
- # update when we're still filling _xput, then after every second
|
|
|
e39309c |
- if (xput_len < self.sma_window or
|
|
|
e39309c |
- now - self._avg_update_ts > 1):
|
|
|
e39309c |
- self.avg = sum(self._xput) / len(self._xput)
|
|
|
e39309c |
- self._avg_update_ts = now
|
|
|
22856fa |
-
|
|
|
22856fa |
def update(self):
|
|
|
22856fa |
pass
|
|
|
22856fa |
|
|
|
e39309c |
@@ -112,10 +141,7 @@ class Infinite(object):
|
|
|
e39309c |
return self.file.isatty() if self.check_tty else True
|
|
|
22856fa |
|
|
|
22856fa |
def next(self, n=1):
|
|
|
e39309c |
- now = monotonic()
|
|
|
22856fa |
- dt = now - self._ts
|
|
|
22856fa |
- self.update_avg(n, dt)
|
|
|
22856fa |
- self._ts = now
|
|
|
22856fa |
+ self.window.next(n, time())
|
|
|
22856fa |
self.index = self.index + n
|
|
|
22856fa |
self.update()
|
|
|
22856fa |
|