On Wed, 10 Apr 2002, Hrvoje Niksic wrote:

> "Roger L. Beeman" <[EMAIL PROTECTED]> writes:
>
> > On Wed, 10 Apr 2002, Hrvoje Niksic wrote:
> >
> >> Agreed wholeheartedly, but how would you *implement* a non-jittering
> >> ETA?  Do you think it makes sense the way 1.8.1 does it, i.e. to
> >> calculate the ETA from the average speed?
> >
> > One common programming technique is the exponential decay model.
>
> Sounds cool.  Do you have a pseudocode or, failing that, a reference
> easy enough that even a programmer of Unix command-line utilities can
> follow it?  :-)
>
> (I must admit that your email address adds a certain weight to
> whatever you have to say about measuring bandwidth.)
>
> > I believe that the method is chosen for its simplicity and that
> > justifications of its validity are completely after the fact. The
> > simplicity is that one keeps a previously calculated value and
> > averages that value with the current measurement and saves the
> > result for the next iteration, i.e. add and shift right.
>
> I thought about calculating the average between the "average" and the
> "current" speed, and use that for ETA, but it sounded too arbitrary
> and I didn't have time to gather empirical evidence that it was any
> better than just using average.  Again, I'd be grateful if you could
> provide some code.
>
> > You must chose how to normalize the measurement based on
> > irregularity in the measurement interval, however.
>
> I'm afraid I can't parse this without understanding the algorithm.

Here is some code to illustrate my suggestion. I am posting
here rather than on wget-patches since it is only for that purpose.

An exponential decay model is implemented *only* for the bar mode.

Since math.h is included for the pow function, I had to add -lm
to the link libs in Makefile.

-------- patch against wget 1.8.1+cvs 15-04-2002 --------

--- src/progress.c.orig Tue Apr 16 13:19:06 2002
+++ src/progress.c      Tue Apr 16 13:31:17 2002
@@ -21,6 +21,7 @@

 #include <stdio.h>
 #include <stdlib.h>
+#include <math.h>
 #ifdef HAVE_STRING_H
 # include <string.h>
 #else
@@ -401,6 +402,9 @@
    create_image will overflow the buffer.  */
 #define MINIMUM_SCREEN_WIDTH 45

+#define RATE_INTERVAL 1000    /* 1000mSec */
+#define DECAY_EXPN       2    /* Exponent */
+
 static int screen_width = DEFAULT_SCREEN_WIDTH;

 /* Size of the history table for download speeds. */
@@ -432,6 +436,8 @@
                                   progress bar where the total size
                                   is not known. */

+  double rate_decay;            /* saved rate for exponential decay */
+  long last_dlcount;            /* to calculate accumulated dl bytes */
   /* The following variables (kept in a struct for namespace reasons)
      keep track of recent download speeds.  See bar_update() for
      details.  */
@@ -468,6 +474,8 @@

   bp->initial_length = initial;
   bp->total_length   = total;
+  bp->count          = 0;
+  bp->last_screen_update    = 0;

   /* - 1 because we don't want to use the last screen column. */
   bp->width = screen_width - 1;
@@ -488,7 +496,7 @@
   struct bar_progress *bp = progress;
   struct bar_progress_hist *hist = &bp->hist;
   int force_screen_update = 0;
-  long delta_time = dltime - hist->previous_time;
+/*  long delta_time = dltime - hist->previous_time; */

   bp->count += howmuch;
   if (bp->total_length > 0
@@ -507,6 +515,7 @@
      "read" consists of one or more network reads, up until the point
      where a subinterval is filled. */

+#if 0
   if (hist->times[hist->pos]
       >= DLSPEED_HISTORY_MAX_INTERVAL / DLSPEED_HISTORY_SIZE)
     {
@@ -532,6 +541,7 @@
   hist->summed_times += delta_time;
   hist->summed_bytes += howmuch;
   hist->previous_time = dltime;
+#endif

 #if 0
   /* Sledgehammer check that summed_times and summed_bytes are
@@ -563,6 +573,7 @@
   create_image (bp, dltime);
   display_image (bp->buffer);
   bp->last_screen_update = dltime;
+  bp->last_dlcount = bp->count;
 }

 static void
@@ -593,6 +604,32 @@
 # define MAX(a, b) ((a) >= (b) ? (a) : (b))
 #endif

+/* Trim the download rate as appropriate for the speed.  Appropriate
+   means that if rate is greater than 1K/s, kilobytes are used, and
+   if rate is greater than 1MB/s, megabytes are used.
+
+   UNITS is zero for B/s, one for KB/s, two for MB/s, and three for
+   GB/s.  */
+double
+trim_rate (double raw_rate, int *units)
+{
+  double dlrate;
+
+ dlrate = raw_rate * 1000;
+  if (dlrate < 1024.0)
+    *units = 0;
+  else if (dlrate < 1024.0 * 1024.0)
+    *units = 1, dlrate /= 1024.0;
+  else if (dlrate < 1024.0 * 1024.0 * 1024.0)
+    *units = 2, dlrate /= (1024.0 * 1024.0);
+  else
+    /* Maybe someone will need this one day.  More realistically, it
+       will get tickled by buggy timers. */
+    *units = 3, dlrate /= (1024.0 * 1024.0 * 1024.0);
+
+  return dlrate;
+}
+
 static void
 create_image (struct bar_progress *bp, long dl_total_time)
 {
@@ -625,6 +662,14 @@
   int dlbytes_size = 1 + MAX (size_legible_len, 11);
   int progress_size = bp->width - (4 + 2 + dlbytes_size + 11 + 13);

+  long this_dlinterval;
+  double this_dlrate;
+  double ddlrate;
+  double decay_int;
+  double decay_factor;
+  static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
+  int units = 0;
+
   if (progress_size < 5)
     progress_size = 0;

@@ -701,6 +746,7 @@
   p += strlen (p);

   /* " 1012.45K/s" */
+#if 0
   if (hist->summed_times && hist->summed_bytes)
     {
       static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" };
@@ -712,13 +758,43 @@
     }
   else
     APPEND_LITERAL ("   --.--K/s");
+#endif

+  if (dl_total_time && bp->count)
+    {
+      if (bp->last_screen_update < RATE_INTERVAL)
+        {
+           ddlrate = calc_rate (bp->count, dl_total_time, &units);
+           bp->rate_decay = (double) bp->count / dl_total_time;
+        }
+      else
+        {
+          this_dlinterval = dl_total_time - bp->last_screen_update;
+          this_dlrate = (double) (bp->count - bp->last_dlcount)
+                          / this_dlinterval;
+          decay_int = -((double) this_dlinterval / RATE_INTERVAL);
+          decay_factor = pow (DECAY_EXPN, decay_int);
+          bp->rate_decay = ( (bp->rate_decay - this_dlrate) *
+                             pow (DECAY_EXPN, decay_int) ) +
+                             this_dlrate;
+          ddlrate = trim_rate (bp->rate_decay, &units);
+        }
+      sprintf (p, " %7.2f%s", ddlrate, short_units[units]);
+      p += strlen (p);
+    }
+  else
+    APPEND_LITERAL ("   --.--K/s");
+
+
   /* " ETA xx:xx:xx" */
-  if (bp->total_length > 0 && dl_total_time > 3000)
+/*  if (bp->total_length > 0 && dl_total_time > 3000) */
+  if (bp->total_length > 0 && bp->count > 0)
     {
       long eta;
       int eta_hrs, eta_min, eta_sec;
+      long bytes_remaining = bp->total_length - size;

+#if 0
       /* Don't change the value of ETA more than approximately once
         per second; doing so would cause flashing without providing
         any value to the user. */
@@ -739,7 +815,9 @@
          bp->last_eta_value = eta;
          bp->last_eta_time = dl_total_time;
        }
+#endif

+      eta = MAX (0,(int) (bytes_remaining / (bp->rate_decay * 1000)));
       eta_hrs = eta / 3600, eta %= 3600;
       eta_min = eta / 60,   eta %= 60;
       eta_sec = eta;


-------- end patch --------

Roger L. Beeman

Reply via email to