Files
hdf5/src/H5timer.c
Larry Knox 11dfa25910 Update copyright headers (#2184)
* Updated source file copyright headers to remove "Copyright by the Board of Trustees
of the University of Illinois", which is kept in the top-level COPYING file.
2022-11-01 16:02:27 -05:00

657 lines
21 KiB
C

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Copyright by The HDF Group. *
* All rights reserved. *
* *
* This file is part of HDF5. The full HDF5 copyright notice, including *
* terms governing use, modification, and redistribution, is contained in *
* the COPYING file, which can be found at the root of the source code *
* distribution tree, or in https://www.hdfgroup.org/licenses. *
* If you do not have access to either file, you may request a copy from *
* help@hdfgroup.org. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*-------------------------------------------------------------------------
* Created: H5timer.c
* Aug 21 2006
* Quincey Koziol
*
* Purpose: Internal, platform-independent 'timer' support routines.
*
*-------------------------------------------------------------------------
*/
/****************/
/* Module Setup */
/****************/
#include "H5module.h" /* This source code file is part of the H5 module */
/***********/
/* Headers */
/***********/
#include "H5private.h" /* Generic Functions */
/****************/
/* Local Macros */
/****************/
/* Size of a generated time string.
* Most time strings should be < 20 or so characters (max!) so this should be a
* safe size. Dynamically allocating the correct size would be painful.
*/
#define H5TIMER_TIME_STRING_LEN 1536
/* Conversion factors */
#define H5_SEC_PER_DAY (24.0 * 60.0 * 60.0)
#define H5_SEC_PER_HOUR (60.0 * 60.0)
#define H5_SEC_PER_MIN (60.0)
/******************/
/* Local Typedefs */
/******************/
/********************/
/* Package Typedefs */
/********************/
/********************/
/* Local Prototypes */
/********************/
/*********************/
/* Package Variables */
/*********************/
/*****************************/
/* Library Private Variables */
/*****************************/
/*******************/
/* Local Variables */
/*******************/
/*-------------------------------------------------------------------------
* Function: H5_bandwidth
*
* Purpose: Prints the bandwidth (bytes per second) in a field 10
* characters wide widh four digits of precision like this:
*
* NaN If <=0 seconds
* 1234. TB/s
* 123.4 TB/s
* 12.34 GB/s
* 1.234 MB/s
* 4.000 kB/s
* 1.000 B/s
* 0.000 B/s If NBYTES==0
* 1.2345e-10 For bandwidth less than 1
* 6.7893e+94 For exceptionally large values
* 6.678e+106 For really big values
*
* Return: void
*
* Programmer: Robb Matzke
* Wednesday, August 5, 1998
*
*-------------------------------------------------------------------------
*/
void
H5_bandwidth(char *buf /*out*/, size_t bufsize, double nbytes, double nseconds)
{
double bw;
if (nseconds <= 0.0)
HDstrcpy(buf, " NaN");
else {
bw = nbytes / nseconds;
if (H5_DBL_ABS_EQUAL(bw, 0.0))
HDstrcpy(buf, "0.000 B/s");
else if (bw < 1.0)
HDsnprintf(buf, bufsize, "%10.4e", bw);
else if (bw < (double)H5_KB) {
HDsnprintf(buf, bufsize, "%05.4f", bw);
HDstrcpy(buf + 5, " B/s");
}
else if (bw < (double)H5_MB) {
HDsnprintf(buf, bufsize, "%05.4f", bw / (double)H5_KB);
HDstrcpy(buf + 5, " kB/s");
}
else if (bw < (double)H5_GB) {
HDsnprintf(buf, bufsize, "%05.4f", bw / (double)H5_MB);
HDstrcpy(buf + 5, " MB/s");
}
else if (bw < (double)H5_TB) {
HDsnprintf(buf, bufsize, "%05.4f", bw / (double)H5_GB);
HDstrcpy(buf + 5, " GB/s");
}
else if (bw < (double)H5_PB) {
HDsnprintf(buf, bufsize, "%05.4f", bw / (double)H5_TB);
HDstrcpy(buf + 5, " TB/s");
}
else if (bw < (double)H5_EB) {
HDsnprintf(buf, bufsize, "%05.4f", bw / (double)H5_PB);
HDstrcpy(buf + 5, " PB/s");
}
else {
HDsnprintf(buf, bufsize, "%10.4e", bw);
if (HDstrlen(buf) > 10)
HDsnprintf(buf, bufsize, "%10.3e", bw);
} /* end else-if */
} /* end else */
} /* end H5_bandwidth() */
/*-------------------------------------------------------------------------
* Function: H5_now
*
* Purpose: Retrieves the current time, as seconds after the UNIX epoch.
*
* Return: # of seconds from the epoch (can't fail)
*
* Programmer: Quincey Koziol
* Tuesday, November 28, 2006
*
*-------------------------------------------------------------------------
*/
time_t
H5_now(void)
{
time_t now; /* Current time */
#ifdef H5_HAVE_GETTIMEOFDAY
{
struct timeval now_tv;
HDgettimeofday(&now_tv, NULL);
now = now_tv.tv_sec;
}
#else /* H5_HAVE_GETTIMEOFDAY */
now = HDtime(NULL);
#endif /* H5_HAVE_GETTIMEOFDAY */
return (now);
} /* end H5_now() */
/*-------------------------------------------------------------------------
* Function: H5_now_usec
*
* Purpose: Retrieves the current time, as microseconds after the UNIX epoch.
*
* Return: # of microseconds from the epoch (can't fail)
*
* Programmer: Quincey Koziol
* Tuesday, November 28, 2006
*
*-------------------------------------------------------------------------
*/
uint64_t
H5_now_usec(void)
{
uint64_t now; /* Current time, in microseconds */
#if defined(H5_HAVE_CLOCK_GETTIME)
{
struct timespec ts;
HDclock_gettime(CLOCK_MONOTONIC, &ts);
/* Cast all values in this expression to uint64_t to ensure that all intermediate
* calculations are done in 64 bit, to prevent overflow */
now = ((uint64_t)ts.tv_sec * ((uint64_t)1000 * (uint64_t)1000)) +
((uint64_t)ts.tv_nsec / (uint64_t)1000);
}
#elif defined(H5_HAVE_GETTIMEOFDAY)
{
struct timeval now_tv;
HDgettimeofday(&now_tv, NULL);
/* Cast all values in this expression to uint64_t to ensure that all intermediate
* calculations are done in 64 bit, to prevent overflow */
now = ((uint64_t)now_tv.tv_sec * ((uint64_t)1000 * (uint64_t)1000)) + (uint64_t)now_tv.tv_usec;
}
#else /* H5_HAVE_GETTIMEOFDAY */
/* Cast all values in this expression to uint64_t to ensure that all intermediate calculations
* are done in 64 bit, to prevent overflow */
now = ((uint64_t)HDtime(NULL) * ((uint64_t)1000 * (uint64_t)1000));
#endif /* H5_HAVE_GETTIMEOFDAY */
return (now);
} /* end H5_now_usec() */
/*--------------------------------------------------------------------------
* Function: H5_get_time
*
* Purpose: Get the current time, as the time of seconds after the UNIX epoch
*
* Return: Success: A non-negative time value
* Failure: -1.0 (in theory, can't currently fail)
*
* Programmer: Quincey Koziol
* October 05, 2016
*--------------------------------------------------------------------------
*/
double
H5_get_time(void)
{
double ret_value = 0.0;
FUNC_ENTER_NOAPI_NOINIT_NOERR
#if defined(H5_HAVE_CLOCK_GETTIME)
{
struct timespec ts;
HDclock_gettime(CLOCK_MONOTONIC, &ts);
ret_value = (double)ts.tv_sec + ((double)ts.tv_nsec / 1000000000.0);
}
#elif defined(H5_HAVE_GETTIMEOFDAY)
{
struct timeval now_tv;
HDgettimeofday(&now_tv, NULL);
ret_value = (double)now_tv.tv_sec + ((double)now_tv.tv_usec / 1000000.0);
}
#else
ret_value = (double)HDtime(NULL);
#endif
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5_get_time() */
/*-------------------------------------------------------------------------
* Function: H5__timer_get_timevals
*
* Purpose: Internal platform-specific function to get time system,
* user and elapsed time values.
*
* Return: Success: 0
* Failure: -1
*
* Programmer: Dana Robinson
* May 2011
*
*-------------------------------------------------------------------------
*/
static herr_t
H5__timer_get_timevals(H5_timevals_t *times /*in,out*/)
{
/* Sanity check */
HDassert(times);
/* Windows call handles both system/user and elapsed times */
#ifdef H5_HAVE_WIN32_API
if (H5_get_win32_times(times) < 0) {
times->elapsed = -1.0;
times->system = -1.0;
times->user = -1.0;
return -1;
} /* end if */
#else /* H5_HAVE_WIN32_API */
/*************************
* System and user times *
*************************/
#if defined(H5_HAVE_GETRUSAGE)
{
struct rusage res;
if (HDgetrusage(RUSAGE_SELF, &res) < 0)
return -1;
times->system = (double)res.ru_stime.tv_sec + ((double)res.ru_stime.tv_usec / 1.0E6);
times->user = (double)res.ru_utime.tv_sec + ((double)res.ru_utime.tv_usec / 1.0E6);
}
#else
/* No suitable way to get system/user times */
/* This is not an error condition, they just won't be available */
times->system = -1.0;
times->user = -1.0;
#endif
/****************
* Elapsed time *
****************/
times->elapsed = H5_get_time();
#endif /* H5_HAVE_WIN32_API */
return 0;
} /* end H5__timer_get_timevals() */
/*-------------------------------------------------------------------------
* Function: H5_timer_init
*
* Purpose: Initialize a platform-independent timer.
*
* Timer usage is as follows:
*
* 1) Call H5_timer_init(), passing in a timer struct, to set
* up the timer.
*
* 2) Wrap any code you'd like to time with calls to
* H5_timer_start/stop(). For accurate timing, place these
* calls as close to the code of interest as possible. You
* can call start/stop multiple times on the same timer -
* when you do this, H5_timer_get_times() will return time
* values for the current/last session and
* H5_timer_get_total_times() will return the summed times
* of all sessions (see #3 and #4, below).
*
* 3) Use H5_timer_get_times() to get the current system, user
* and elapsed times from a running timer. If called on a
* stopped timer, this will return the time recorded at the
* stop point.
*
* 4) Call H5_timer_get_total_times() to get the total system,
* user and elapsed times recorded across multiple start/stop
* sessions. If called on a running timer, it will return the
* time recorded up to that point. On a stopped timer, it
* will return the time recorded at the stop point.
*
* NOTE: Obtaining a time point is not free! Keep in mind that
* the time functions make system calls and can have
* non-trivial overhead. If you call one of the get_time
* functions on a running timer, that overhead will be
* added to the reported times.
*
* 5) All times recorded will be in seconds. These can be
* converted into human-readable strings with the
* H5_timer_get_time_string() function.
*
* 6) A timer can be reset using by calling H5_timer_init() on
* it. This will set its state to 'stopped' and reset all
* accumulated times to zero.
*
*
* Return: Success: 0
* Failure: -1
*
* Programmer: Dana Robinson
* May 2011
*
*-------------------------------------------------------------------------
*/
herr_t
H5_timer_init(H5_timer_t *timer /*in,out*/)
{
/* Sanity check */
HDassert(timer);
/* Initialize everything */
HDmemset(timer, 0, sizeof(H5_timer_t));
return 0;
} /* end H5_timer_init() */
/*-------------------------------------------------------------------------
* Function: H5_timer_start
*
* Purpose: Start tracking time in a platform-independent timer.
*
* Return: Success: 0
* Failure: -1
*
* Programmer: Dana Robinson
* May 2011
*
*-------------------------------------------------------------------------
*/
herr_t
H5_timer_start(H5_timer_t *timer /*in,out*/)
{
/* Sanity check */
HDassert(timer);
/* Start the timer
* This sets the "initial" times to the system-defined start times.
*/
if (H5__timer_get_timevals(&(timer->initial)) < 0)
return -1;
timer->is_running = TRUE;
return 0;
} /* end H5_timer_start() */
/*-------------------------------------------------------------------------
* Function: H5_timer_stop
*
* Purpose: Stop tracking time in a platform-independent timer.
*
* Return: Success: 0
* Failure: -1
*
* Programmer: Dana Robinson
* May 2011
*
*-------------------------------------------------------------------------
*/
herr_t
H5_timer_stop(H5_timer_t *timer /*in,out*/)
{
/* Sanity check */
HDassert(timer);
/* Stop the timer */
if (H5__timer_get_timevals(&(timer->final_interval)) < 0)
return -1;
/* The "final" times are stored as intervals (final - initial)
* for more useful reporting to the user.
*/
timer->final_interval.elapsed = timer->final_interval.elapsed - timer->initial.elapsed;
timer->final_interval.system = timer->final_interval.system - timer->initial.system;
timer->final_interval.user = timer->final_interval.user - timer->initial.user;
/* Add the intervals to the elapsed time */
timer->total.elapsed += timer->final_interval.elapsed;
timer->total.system += timer->final_interval.system;
timer->total.user += timer->final_interval.user;
timer->is_running = FALSE;
return 0;
} /* end H5_timer_stop() */
/*-------------------------------------------------------------------------
* Function: H5_timer_get_times
*
* Purpose: Get the system, user and elapsed times from a timer. These
* are the times since the timer was last started and will be
* 0.0 in a timer that has not been started since it was
* initialized.
*
* This function can be called either before or after
* H5_timer_stop() has been called. If it is called before the
* stop function, the timer will continue to run.
*
* The system and user times will be -1.0 if those times cannot
* be computed on a particular platform. The elapsed time will
* always be present.
*
* Return: Success: 0
* Failure: -1
*
* Programmer: Dana Robinson
* May 2011
*
*-------------------------------------------------------------------------
*/
herr_t
H5_timer_get_times(H5_timer_t timer, H5_timevals_t *times /*in,out*/)
{
/* Sanity check */
HDassert(times);
if (timer.is_running) {
H5_timevals_t now;
/* Get the current times and report the current intervals without
* stopping the timer.
*/
if (H5__timer_get_timevals(&now) < 0)
return -1;
times->elapsed = now.elapsed - timer.initial.elapsed;
times->system = now.system - timer.initial.system;
times->user = now.user - timer.initial.user;
} /* end if */
else {
times->elapsed = timer.final_interval.elapsed;
times->system = timer.final_interval.system;
times->user = timer.final_interval.user;
} /* end else */
return 0;
} /* end H5_timer_get_times() */
/*-------------------------------------------------------------------------
* Function: H5_timer_get_total_times
*
* Purpose: Get the TOTAL system, user and elapsed times recorded by
* the timer since its initialization. This is the sum of all
* times recorded while the timer was running.
*
* These will be 0.0 in a timer that has not been started
* since it was initialized. Calling H5_timer_init() on a
* timer will reset these values to 0.0.
*
* This function can be called either before or after
* H5_timer_stop() has been called. If it is called before the
* stop function, the timer will continue to run.
*
* The system and user times will be -1.0 if those times cannot
* be computed on a particular platform. The elapsed time will
* always be present.
*
* Return: Success: 0
* Failure: -1
*
* Programmer: Dana Robinson
* May 2011
*
*-------------------------------------------------------------------------
*/
herr_t
H5_timer_get_total_times(H5_timer_t timer, H5_timevals_t *times /*in,out*/)
{
/* Sanity check */
HDassert(times);
if (timer.is_running) {
H5_timevals_t now;
/* Get the current times and report the current totals without
* stopping the timer.
*/
if (H5__timer_get_timevals(&now) < 0)
return -1;
times->elapsed = timer.total.elapsed + (now.elapsed - timer.initial.elapsed);
times->system = timer.total.system + (now.system - timer.initial.system);
times->user = timer.total.user + (now.user - timer.initial.user);
} /* end if */
else {
times->elapsed = timer.total.elapsed;
times->system = timer.total.system;
times->user = timer.total.user;
} /* end else */
return 0;
} /* end H5_timer_get_total_times() */
/*-------------------------------------------------------------------------
* Function: H5_timer_get_time_string
*
* Purpose: Converts a time (in seconds) into a human-readable string
* suitable for log messages.
*
* Return: Success: The time string.
*
* The general format of the time string is:
*
* "N/A" time < 0 (invalid time)
* "%.f ns" time < 1 microsecond
* "%.1f us" time < 1 millisecond
* "%.1f ms" time < 1 second
* "%.2f s" time < 1 minute
* "%.f m %.f s" time < 1 hour
* "%.f h %.f m %.f s" longer times
*
* Failure: NULL
*
* Programmer: Dana Robinson
* May 2011
*
*-------------------------------------------------------------------------
*/
char *
H5_timer_get_time_string(double seconds)
{
char *s; /* output string */
/* Used when the time is greater than 59 seconds */
double days = 0.0;
double hours = 0.0;
double minutes = 0.0;
double remainder_sec = 0.0;
/* Extract larger time units from count of seconds */
if (seconds > 60.0) {
/* Set initial # of seconds */
remainder_sec = seconds;
/* Extract days */
days = HDfloor(remainder_sec / H5_SEC_PER_DAY);
remainder_sec -= (days * H5_SEC_PER_DAY);
/* Extract hours */
hours = HDfloor(remainder_sec / H5_SEC_PER_HOUR);
remainder_sec -= (hours * H5_SEC_PER_HOUR);
/* Extract minutes */
minutes = HDfloor(remainder_sec / H5_SEC_PER_MIN);
remainder_sec -= (minutes * H5_SEC_PER_MIN);
/* The # of seconds left is in remainder_sec */
} /* end if */
/* Allocate */
if (NULL == (s = (char *)HDcalloc(H5TIMER_TIME_STRING_LEN, sizeof(char))))
return NULL;
/* Do we need a format string? Some people might like a certain
* number of milliseconds or s before spilling to the next highest
* time unit. Perhaps this could be passed as an integer.
* (name? round_up_size? ?)
*/
if (seconds < 0.0)
HDsnprintf(s, H5TIMER_TIME_STRING_LEN, "N/A");
else if (H5_DBL_ABS_EQUAL(0.0, seconds))
HDsnprintf(s, H5TIMER_TIME_STRING_LEN, "0.0 s");
else if (seconds < 1.0E-6)
/* t < 1 us, Print time in ns */
HDsnprintf(s, H5TIMER_TIME_STRING_LEN, "%.f ns", seconds * 1.0E9);
else if (seconds < 1.0E-3)
/* t < 1 ms, Print time in us */
HDsnprintf(s, H5TIMER_TIME_STRING_LEN, "%.1f us", seconds * 1.0E6);
else if (seconds < 1.0)
/* t < 1 s, Print time in ms */
HDsnprintf(s, H5TIMER_TIME_STRING_LEN, "%.1f ms", seconds * 1.0E3);
else if (seconds < H5_SEC_PER_MIN)
/* t < 1 m, Print time in s */
HDsnprintf(s, H5TIMER_TIME_STRING_LEN, "%.2f s", seconds);
else if (seconds < H5_SEC_PER_HOUR)
/* t < 1 h, Print time in m and s */
HDsnprintf(s, H5TIMER_TIME_STRING_LEN, "%.f m %.f s", minutes, remainder_sec);
else if (seconds < H5_SEC_PER_DAY)
/* t < 1 d, Print time in h, m and s */
HDsnprintf(s, H5TIMER_TIME_STRING_LEN, "%.f h %.f m %.f s", hours, minutes, remainder_sec);
else
/* Print time in d, h, m and s */
HDsnprintf(s, H5TIMER_TIME_STRING_LEN, "%.f d %.f h %.f m %.f s", days, hours, minutes,
remainder_sec);
return s;
} /* end H5_timer_get_time_string() */