Convert ptimers to use seconds instead of milliseconds
Hrvoje Niksic <[email protected]> Wed, 06 Jul 2005 18:35:10 +0200
| Newsgroups | gmane.comp.web.wget.patches |
|---|---|
| Message-ID | <[email protected]> |
The ptimers originally returned "long" amounts of milliseconds, which allowed them to be fairly precise without resorting to floating-point arithmetic. With the advent of more reliable timing and sleeping functions (clock_gettime and nanosleep), they were converted to return "double" -- but in that case the milliseconds are only a hindrance. 2005-07-06 Hrvoje Niksic <[email protected]> * ptimer.c: Measure time in seconds rather than milliseconds. Adjusted all callers. Index: src/progress.c =================================================================== --- src/progress.c (revision 1958) +++ src/progress.c (working copy) @@ -175,7 +175,7 @@ } /* Inform the progress gauge of newly received bytes. DLTIME is the - time in milliseconds since the beginning of the download. */ + time since the beginning of the download. */ void progress_update (void *progress, wgint howmuch, double dltime) @@ -320,20 +320,18 @@ wgint bytes_remaining = dp->total_length - bytes_displayed; /* The quantity downloaded in this download run. */ wgint bytes_sofar = bytes_displayed - dp->initial_length; - double secs_sofar = dltime / 1000; - int eta = (int) (secs_sofar * bytes_remaining / bytes_sofar + 0.5); + int eta = (int) (dltime * bytes_remaining / bytes_sofar + 0.5); logprintf (LOG_VERBOSE, " %s", eta_to_human_short (eta, true)); } } else { /* When done, print the total download time */ - double secs = dltime / 1000; - if (secs >= 10) + if (dltime >= 10) logprintf (LOG_VERBOSE, "=%s", - eta_to_human_short ((int) (secs + 0.5), true)); + eta_to_human_short ((int) (dltime + 0.5), true)); else - logprintf (LOG_VERBOSE, "=%ss", print_decimal (secs)); + logprintf (LOG_VERBOSE, "=%ss", print_decimal (dltime)); } } @@ -478,12 +476,12 @@ sample is at least 150ms long, which means that, over the course of 20 samples, "current" download speed spans at least 3s into the past. */ -#define DLSPEED_SAMPLE_MIN 150 +#define DLSPEED_SAMPLE_MIN 0.15 /* The time after which the download starts to be considered "stalled", i.e. the current bandwidth is not printed and the recent download speeds are scratched. */ -#define STALL_START_TIME 5000 +#define STALL_START_TIME 5 struct bar_progress { wgint initial_length; /* how many bytes have been downloaded @@ -513,12 +511,12 @@ details. */ struct bar_progress_hist { int pos; - wgint times[DLSPEED_HISTORY_SIZE]; + double times[DLSPEED_HISTORY_SIZE]; wgint bytes[DLSPEED_HISTORY_SIZE]; /* The sum of times and bytes respectively, maintained for efficiency. */ - wgint total_time; + double total_time; wgint total_bytes; } hist; @@ -618,7 +616,7 @@ received_sigwinch = 0; } - if (dltime - bp->last_screen_update < 200 && !force_screen_update) + if (dltime - bp->last_screen_update < 0.2 && !force_screen_update) /* Don't update more often than five times per second. */ return; @@ -708,7 +706,7 @@ value because the current bandwidth would start too small. Start with an arbitrary (but more reasonable) time value and let it level out. */ - recent_age = 1000; + recent_age = 1; } /* Store "recent" bytes and download time to history ring at the @@ -743,8 +741,14 @@ sumt += hist->times[i]; sumb += hist->bytes[i]; } - assert (sumt == hist->total_time); assert (sumb == hist->total_bytes); + /* We can't use assert(sumt==hist->total_time) because some + precision is lost by adding and subtracting floating-point + numbers. But during a download this precision should not be + detectable, i.e. no larger than 1ns. */ + double diff = sumt - hist->total_time; + if (diff < 0) diff = -diff; + assert (diff < 1e-9); } #endif } @@ -879,7 +883,7 @@ move_to_end (p); /* " 12.52K/s" */ - if (hist->total_time && hist->total_bytes) + if (hist->total_time > 0 && hist->total_bytes) { static const char *short_units[] = { "B/s", "K/s", "M/s", "G/s" }; int units = 0; @@ -900,7 +904,7 @@ /* " eta ..m ..s"; wait for three seconds before displaying the ETA. That's because the ETA value needs a while to become reliable. */ - if (bp->total_length > 0 && bp->count > 0 && dl_total_time > 3000) + if (bp->total_length > 0 && bp->count > 0 && dl_total_time > 3) { int eta; @@ -909,7 +913,7 @@ any value to the user. */ if (bp->total_length != size && bp->last_eta_value != 0 - && dl_total_time - bp->last_eta_time < 900) + && dl_total_time - bp->last_eta_time < 0.9) eta = bp->last_eta_value; else { @@ -919,9 +923,8 @@ hist->total_time and bp->count with hist->total_bytes. I found that doing that results in a very jerky and ultimately unreliable ETA. */ - double time_sofar = (double) dl_total_time / 1000; wgint bytes_remaining = bp->total_length - size; - eta = (int) (time_sofar * bytes_remaining / bp->count + 0.5); + eta = (int) (dl_total_time * bytes_remaining / bp->count + 0.5); bp->last_eta_value = eta; bp->last_eta_time = dl_total_time; } @@ -939,15 +942,15 @@ else { /* When the download is done, print the elapsed time. */ - double secs = dl_total_time / 1000; + /* Note to translators: this should not take up more room than available here. Abbreviate if necessary. */ strcpy (p, _(" in ")); move_to_end (p); /* not p+=6, think translations! */ - if (secs >= 10) - strcpy (p, eta_to_human_short ((int) (secs + 0.5), false)); + if (dl_total_time >= 10) + strcpy (p, eta_to_human_short ((int) (dl_total_time + 0.5), false)); else - sprintf (p, "%ss", print_decimal (secs)); + sprintf (p, "%ss", print_decimal (dl_total_time)); move_to_end (p); } Index: src/retr.c =================================================================== --- src/retr.c (revision 1958) +++ src/retr.c (working copy) @@ -55,7 +55,7 @@ /* Total size of downloaded files. Used to enforce quota. */ SUM_SIZE_INT total_downloaded_bytes; -/* Total download time in milliseconds. */ +/* Total download time in seconds. */ double total_download_time; /* If non-NULL, the stream to which output should be written. This @@ -75,9 +75,7 @@ static void limit_bandwidth_reset (void) { - limit_data.chunk_bytes = 0; - limit_data.chunk_start = 0; - limit_data.sleep_adjust = 0; + xzero (limit_data); } /* Limit the bandwidth by pausing the download for an amount of time. @@ -95,25 +93,25 @@ /* Calculate the amount of time we expect downloading the chunk should take. If in reality it took less time, sleep to compensate for the difference. */ - expected = 1000.0 * limit_data.chunk_bytes / opt.limit_rate; + expected = (double) limit_data.chunk_bytes / opt.limit_rate; if (expected > delta_t) { double slp = expected - delta_t + limit_data.sleep_adjust; double t0, t1; - if (slp < 200) + if (slp < 0.2) { DEBUGP (("deferring a %.2f ms sleep (%s/%.2f).\n", - slp, number_to_static_string (limit_data.chunk_bytes), + slp * 1000, number_to_static_string (limit_data.chunk_bytes), delta_t)); return; } DEBUGP (("\nsleeping %.2f ms for %s bytes, adjust %.2f ms\n", - slp, number_to_static_string (limit_data.chunk_bytes), + slp * 1000, number_to_static_string (limit_data.chunk_bytes), limit_data.sleep_adjust)); t0 = ptimer_read (timer); - xsleep (slp / 1000); + xsleep (slp); t1 = ptimer_measure (timer); /* Due to scheduling, we probably slept slightly longer (or @@ -123,10 +121,10 @@ limit_data.sleep_adjust = slp - (t1 - t0); /* If sleep_adjust is very large, it's likely due to suspension and not clock inaccuracy. Don't enforce those. */ - if (limit_data.sleep_adjust > 500) - limit_data.sleep_adjust = 500; - else if (limit_data.sleep_adjust < -500) - limit_data.sleep_adjust = -500; + if (limit_data.sleep_adjust > 0.5) + limit_data.sleep_adjust = 0.5; + else if (limit_data.sleep_adjust < -0.5) + limit_data.sleep_adjust = -0.5; } limit_data.chunk_bytes = 0; @@ -185,7 +183,7 @@ is incremented by the amount of data read from the network. If QTYWRITTEN is non-NULL, the value it points to is incremented by the amount of data written to disk. The time it took to download - the data (in milliseconds) is stored to ELAPSED. + the data is stored to ELAPSED. The function exits and returns the amount of data read. In case of error while reading data, -1 is returned. In case of error while @@ -267,7 +265,7 @@ if (opt.read_timeout) { double waittm; - waittm = (ptimer_read (timer) - last_successful_read_tm) / 1000; + waittm = ptimer_read (timer) - last_successful_read_tm; if (waittm + tmout > opt.read_timeout) { /* Don't let total idle time exceed read timeout. */ @@ -540,22 +538,23 @@ UNITS is zero for B/s, one for KB/s, two for MB/s, and three for GB/s. */ + double -calc_rate (wgint bytes, double msecs, int *units) +calc_rate (wgint bytes, double secs, int *units) { double dlrate; - assert (msecs >= 0); + assert (secs >= 0); assert (bytes >= 0); - if (msecs == 0) + if (secs == 0) /* If elapsed time is exactly zero, it means we're under the resolution of the timer. This can easily happen on systems that use time() for the timer. Since the interval lies between 0 and the timer's resolution, assume half the resolution. */ - msecs = ptimer_resolution () / 2.0; + secs = ptimer_resolution () / 2.0; - dlrate = 1000.0 * bytes / msecs; + dlrate = bytes / secs; if (dlrate < 1024.0) *units = 0; else if (dlrate < 1024.0 * 1024.0) Index: src/ptimer.c =================================================================== --- src/ptimer.c (revision 1958) +++ src/ptimer.c (working copy) @@ -39,16 +39,16 @@ ptimer_destroy -- destroy the timer. ptimer_granularity -- returns the approximate granularity of the timers. - Timers measure time in milliseconds, but the timings they return - are floating point numbers, so they can carry as much precision as - the underlying system timer supports. For example, to measure the - time it takes to run a loop, you can use something like: + Timers measure time in seconds, returning the timings as floating + point numbers, so they can carry as much precision as the + underlying system timer supports. For example, to measure the time + it takes to run a loop, you can use something like: ptimer *tmr = ptimer_new (); while (...) ... loop ... - double msecs = ptimer_measure (); - printf ("The loop took %.2f ms\n", msecs); */ + double secs = ptimer_measure (); + printf ("The loop took %.2fs\n", secs); */ #include <config.h> @@ -108,8 +108,8 @@ CLOCK_MONOTONIC where available, CLOCK_REALTIME otherwise. */ static int posix_clock_id; -/* Resolution of the clock, in milliseconds. */ -static double posix_millisec_resolution; +/* Resolution of the clock, initialized in posix_init. */ +static double posix_clock_resolution; /* Decide which clock_id to use. */ @@ -148,11 +148,10 @@ if (clock_getres (clocks[i].id, &r) < 0) continue; /* clock_getres doesn't work for this clock */ posix_clock_id = clocks[i].id; - posix_millisec_resolution = r.tv_sec * 1000.0 + r.tv_nsec / 1000000.0; - /* Guard against broken clock_getres returning nonsensical - values. */ - if (posix_millisec_resolution == 0) - posix_millisec_resolution = 1; + posix_clock_resolution = (double) r.tv_sec + r.tv_nsec / 1e9; + /* Guard against nonsense returned by a broken clock_getres. */ + if (posix_clock_resolution == 0) + posix_clock_resolution = 1e-3; break; } if (i == countof (clocks)) @@ -163,7 +162,7 @@ strerror (errno)); /* Use CLOCK_REALTIME, but invent a plausible resolution. */ posix_clock_id = CLOCK_REALTIME; - posix_millisec_resolution = 1; + posix_clock_resolution = 1e-3; } } @@ -176,14 +175,14 @@ static inline double posix_diff (ptimer_system_time *pst1, ptimer_system_time *pst2) { - return ((pst1->tv_sec - pst2->tv_sec) * 1000.0 - + (pst1->tv_nsec - pst2->tv_nsec) / 1000000.0); + return ((pst1->tv_sec - pst2->tv_sec) + + (pst1->tv_nsec - pst2->tv_nsec) / 1e9); } static inline double posix_resolution (void) { - return posix_millisec_resolution; + return posix_clock_resolution; } #endif /* PTIMER_POSIX */ @@ -209,8 +208,8 @@ static inline double gettimeofday_diff (ptimer_system_time *pst1, ptimer_system_time *pst2) { - return ((pst1->tv_sec - pst2->tv_sec) * 1000.0 - + (pst1->tv_usec - pst2->tv_usec) / 1000.0); + return ((pst1->tv_sec - pst2->tv_sec) + + (pst1->tv_usec - pst2->tv_usec) / 1e6); } static inline double @@ -246,9 +245,9 @@ static bool windows_hires_timers; /* Frequency of high-resolution timers -- number of updates per - millisecond. Calculated the first time ptimer_new is called - provided that high-resolution timers are available. */ -static double windows_hires_msfreq; + second. Calculated the first time ptimer_new is called provided + that high-resolution timers are available. */ +static double windows_hires_freq; static void windows_init (void) @@ -259,7 +258,7 @@ if (freq.QuadPart != 0) { windows_hires_timers = true; - windows_hires_msfreq = (double) freq.QuadPart / 1000.0; + windows_hires_freq = (double) freq.QuadPart; } } @@ -281,7 +280,7 @@ windows_diff (ptimer_system_time *pst1, ptimer_system_time *pst2) { if (windows_hires_timers) - return (pst1->hires.QuadPart - pst2->hires.QuadPart) / windows_hires_msfreq; + return (pst1->hires.QuadPart - pst2->hires.QuadPart) / windows_hires_freq; else return pst1->lores - pst2->lores; } @@ -290,7 +289,7 @@ windows_resolution (void) { if (windows_hires_timers) - return 1.0 / windows_hires_msfreq; + return 1.0 / windows_hires_freq; else return 10; /* according to MSDN */ } @@ -303,8 +302,7 @@ time, yields elapsed time. */ ptimer_system_time start; - /* The most recent elapsed time, calculated by ptimer_measure(). - Measured in milliseconds. */ + /* The most recent elapsed time, calculated by ptimer_measure(). */ double elapsed_last; /* Approximately, the time elapsed between the true start of the @@ -341,8 +339,8 @@ } /* Reset timer PT. This establishes the starting point from which - ptimer_read() will return the number of elapsed milliseconds. - It is allowed to reset a previously used timer. */ + ptimer_measure() will return the elapsed time in seconds. It is + allowed to reset a previously used timer. */ void ptimer_reset (struct ptimer *pt) @@ -353,14 +351,11 @@ pt->elapsed_pre_start = 0; } -/* Measure the elapsed time since timer creation/reset and return it - to the caller. The value remains stored for further reads by - ptimer_read. +/* Measure the elapsed time since timer creation/reset. This causes + the timer to internally call clock_gettime (or gettimeofday, etc.) + to update its idea of current time. The time is returned, but is + also stored for later access through ptimer_read(). - This function causes the timer to call gettimeofday (or time(), - etc.) to update its idea of current time. To get the elapsed - interval in milliseconds, use ptimer_read. - This function handles clock skew, i.e. time that moves backwards is ignored. */ @@ -400,8 +395,9 @@ return elapsed; } -/* Return the elapsed time in milliseconds between the last call to - ptimer_reset and the last call to ptimer_update. */ +/* Return the most recent elapsed time measured with ptimer_measure. + If ptimer_measure has not yet been called since the timer was + created or reset, this returns 0. */ double ptimer_read (const struct ptimer *pt) @@ -410,8 +406,8 @@ } /* Return the assessed resolution of the timer implementation, in - milliseconds. This is used by code that tries to substitute a - better value for timers that have returned zero. */ + seconds. This is used by code that tries to substitute a better + value for timers that have returned zero. */ double ptimer_resolution (void) Index: src/convert.c =================================================================== --- src/convert.c (revision 1958) +++ src/convert.c (working copy) @@ -166,7 +166,7 @@ free_urlpos (urls); } - secs = ptimer_measure (timer) / 1000; + secs = ptimer_measure (timer); ptimer_destroy (timer); logprintf (LOG_VERBOSE, _("Converted %d files in %s seconds.\n"), file_count, print_decimal (secs)); Index: src/main.c =================================================================== --- src/main.c (revision 1958) +++ src/main.c (working copy) @@ -974,7 +974,7 @@ time_str (NULL), opt.numurls, human_readable (total_downloaded_bytes), - secs_to_human_time (total_download_time / 1000), + secs_to_human_time (total_download_time), retr_rate (total_downloaded_bytes, total_download_time)); /* Print quota warning, if exceeded. */ if (opt.quota && total_downloaded_bytes > opt.quota)