Merge pull request #65 from lemonsqueeze/eta_fix

Alter ETA calculation to use the rate over the past 30 seconds instead of over the whole transfer, and allow that 30 second window to be adjusted.
This commit is contained in:
Andrew Wood
2023-07-16 11:46:23 +01:00
committed by GitHub
9 changed files with 107 additions and 29 deletions
+8 -3
View File
@@ -127,7 +127,7 @@ Turn the timer on. This will display the total elapsed time that
has been running for.
.TP
.B \-e, \-\-eta
Turn the ETA timer on. This will attempt to guess, based on previous
Turn the ETA timer on. This will attempt to guess, based on current
transfer rates and the total data size, how long it will be before
completion. This option will have no effect if the total data size cannot
be determined.
@@ -142,8 +142,8 @@ Turn the rate counter on. This will display the current rate of data
transfer.
.TP
.B \-a, \-\-average\-rate
Turn the average rate counter on. This will display the average rate of
data transfer so far.
Turn the average rate counter on. This will display the current average
rate of data transfer (default: last 30s, see --eta-window).
.TP
.B \-b, \-\-bytes
Turn the total byte counter on. This will display the total amount of
@@ -276,6 +276,11 @@ Wait
seconds between updates. The default is to update every second.
Note that this can be a decimal such as 0.1.
.TP
.B \-m SEC, \-\-eta-window SEC
Compute current average rate over a
.B SEC
seconds window for ETA calculation (default 30s).
.TP
.B \-w WIDTH, \-\-width WIDTH
Assume the terminal is
.B WIDTH
+1
View File
@@ -42,6 +42,7 @@ struct opts_s { /* structure describing run-time options */
double delay_start; /* delay before first display */
unsigned int watch_pid; /* process to watch fds of */
int watch_fd; /* fd to watch */
unsigned int eta_window; /* time window in seconds for eta calculations */
unsigned int width; /* screen width */
unsigned int height; /* screen height */
char *name; /* process name, if any */
+15
View File
@@ -46,6 +46,12 @@ extern "C" {
#define MAXIMISE_BUFFER_FILL 1
typedef struct pvhistory {
long long total_bytes;
long double elapsed_sec;
} pvhistory_t;
/*
* Structure for holding PV internal state. Opaque outside the PV library.
*/
@@ -115,6 +121,15 @@ struct pvstate_s {
long double prev_elapsed_sec;
long double prev_rate;
long double prev_trans;
/* Keep track of progress over last intervals to compute current average rate. */
pvhistory_t *history; /* state at previous intervals (circular buffer) */
int history_len; /* total size */
int history_interval; /* seconds between each history entry */
int history_first;
int history_last;
long double current_avg_rate; /* current average rate over last history intervals */
unsigned long long initial_offset;
char *display_buffer;
long display_buffer_size;
+1
View File
@@ -93,6 +93,7 @@ extern void pv_state_name_set(pvstate_t, const char *);
extern void pv_state_format_string_set(pvstate_t, const char *);
extern void pv_state_watch_pid_set(pvstate_t, unsigned int);
extern void pv_state_watch_fd_set(pvstate_t, int);
extern void pv_state_eta_window_set(pvstate_t, int);
extern void pv_state_inputfiles(pvstate_t, int, const char **);
+2
View File
@@ -40,6 +40,8 @@ void display_help(void)
N_("show data transfer rate counter")},
{"-a", "--average-rate", 0,
N_("show data transfer average rate counter")},
{"-m", "--eta-window", N_("SEC"),
N_("compute current average rate over a SEC seconds window for ETA (default 30s)")},
{"-b", "--bytes", 0,
N_("show number of bytes transferred")},
{"-T", "--buffer-percent", 0,
+1
View File
@@ -206,6 +206,7 @@ int main(int argc, char **argv)
pv_state_format_string_set(state, opts->format);
pv_state_watch_pid_set(state, opts->watch_pid);
pv_state_watch_fd_set(state, opts->watch_fd);
pv_state_eta_window_set(state, opts->eta_window);
pv_state_set_format(state, opts->progress, opts->timer, opts->eta,
opts->fineta, opts->rate, opts->average_rate,
+7 -1
View File
@@ -82,12 +82,13 @@ opts_t opts_parse(int argc, char **argv)
{"remote", 1, NULL, (int) 'R'},
{"pidfile", 1, NULL, (int) 'P'},
{"watchfd", 1, NULL, (int) 'd'},
{"eta-window", 1, NULL, (int) 'm'},
{NULL, 0, NULL, 0}
};
int option_index = 0;
#endif
char *short_options =
"hVpteIrabTA:fnqcWD:s:l0i:w:H:N:F:L:B:CESR:P:d:";
"hVpteIrabTA:fnqcWD:s:l0i:w:H:N:F:L:B:CESR:P:d:m:";
int c, numopts;
unsigned int check_pid;
int check_fd;
@@ -123,6 +124,7 @@ opts_t opts_parse(int argc, char **argv)
opts->delay_start = 0;
opts->watch_pid = 0;
opts->watch_fd = -1;
opts->eta_window = 30;
do {
#ifdef HAVE_GETOPT_LONG
@@ -149,6 +151,7 @@ opts_t opts_parse(int argc, char **argv)
case 'L':
case 'B':
case 'R':
case 'm':
if (pv_getnum_check(optarg, PV_NUMTYPE_INTEGER) !=
0) {
fprintf(stderr, "%s: -%c: %s\n",
@@ -337,6 +340,9 @@ opts_t opts_parse(int argc, char **argv)
(void) sscanf(optarg, "%u:%d", &(opts->watch_pid),
&(opts->watch_fd));
break;
case 'm':
opts->eta_window = pv_getnum_ui(optarg);
break;
default:
#ifdef HAVE_GETOPT_LONG
fprintf(stderr,
+40 -25
View File
@@ -99,16 +99,14 @@ static long pv__calc_percentage(long long so_far, const long long total)
* number of seconds until completion.
*/
static long pv__calc_eta(const long long so_far, const long long total,
const long elapsed)
const long rate)
{
long long amount_left;
if (so_far < 1)
if (so_far < 1 || !rate)
return 0;
amount_left = total - so_far;
amount_left *= (long long) elapsed;
amount_left /= so_far;
amount_left = (total - so_far) / rate;
return (long) amount_left;
}
@@ -434,6 +432,38 @@ static long bound_long(long x, long min, long max)
return x < min ? min : x > max ? max : x;
}
/* Update history and current average rate */
static void update_history_avg_rate(pvstate_t state, long long total_bytes,
long double elapsed_sec, long double rate)
{
int first = state->history_first;
int last = state->history_last;
long double last_elapsed = state->history[last].elapsed_sec;
if (!(last_elapsed == 0.0 || /* Empty */
elapsed_sec > last_elapsed + state->history_interval))
return;
if (last_elapsed) { /* Not empty, add new entry in circular buffer */
int len = state->history_len;
state->history_last = last = (last + 1) % len;
if (last == first)
state->history_first = first = (first + 1) % len;
}
state->history[last].elapsed_sec = elapsed_sec;
state->history[last].total_bytes = total_bytes;
if (first == last)
state->current_avg_rate = rate;
else {
long long bytes = (state->history[last].total_bytes -
state->history[first].total_bytes);
long double sec = (state->history[last].elapsed_sec -
state->history[first].elapsed_sec);
state->current_avg_rate = bytes / sec;
}
}
/*
* Return a pointer to a string (which must not be freed), containing status
@@ -494,24 +524,9 @@ static char *pv__format(pvstate_t state,
}
state->prev_rate = rate;
/*
* We only calculate the overall average rate if this is the last
* update or if the average rate display is enabled. Otherwise it's
* not worth the extra CPU cycles.
*/
average_rate = 0;
if ((bytes_since_last < 0)
|| ((state->components_used & PV_DISPLAY_AVERAGERATE) != 0)) {
/* Sanity check to avoid division by zero */
if (elapsed_sec < 0.000001)
elapsed_sec = 0.000001;
average_rate =
(((long double) total_bytes) -
((long double) state->initial_offset)) /
(long double) elapsed_sec;
if (bytes_since_last < 0)
rate = average_rate;
}
/* Update history and current average rate for ETA. */
update_history_avg_rate(state, total_bytes, elapsed_sec, rate);
average_rate = state->current_avg_rate;
if (state->size <= 0) {
/*
@@ -684,7 +699,7 @@ static char *pv__format(pvstate_t state,
eta =
pv__calc_eta(total_bytes - state->initial_offset,
state->size - state->initial_offset,
elapsed_sec);
state->current_avg_rate);
/*
* Bounds check, so we don't overrun the suffix buffer. This
@@ -736,7 +751,7 @@ static char *pv__format(pvstate_t state,
eta =
pv__calc_eta(total_bytes - state->initial_offset,
state->size - state->initial_offset,
elapsed_sec);
state->current_avg_rate);
/*
* Bounds check, so we don't overrun the suffix buffer. This
+32
View File
@@ -7,9 +7,24 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
/* alloc / realloc history buffer */
static void pv_alloc_history(pvstate_t state)
{
if (state->history)
free(state->history);
assert(state->history_len);
assert(state->history_interval);
state->history = calloc(state->history_len, sizeof(state->history[0]));
state->history_first = state->history_last = 0;
state->history[0].elapsed_sec = 0.0; /* to be safe, memset() not recommended for doubles */
}
/*
* Create a new state structure, and return it, or 0 (NULL) on error.
*/
@@ -70,6 +85,10 @@ void pv_state_free(pvstate_t state)
free(state->transfer_buffer);
state->transfer_buffer = NULL;
if (state->history)
free(state->history);
state->history = NULL;
free(state);
return;
@@ -225,6 +244,19 @@ void pv_state_watch_fd_set(pvstate_t state, int val)
state->watch_fd = val;
};
void pv_state_eta_window_set(pvstate_t state, int val)
{
if (val >= 20) {
state->history_len = val / 5 + 1;
state->history_interval = 5;
}
else {
state->history_len = val + 1;
state->history_interval = 1;
}
pv_alloc_history(state);
};
/*
* Set the array of input files.