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