diff options
Diffstat (limited to 'src/include/portability/instr_time.h')
-rw-r--r-- | src/include/portability/instr_time.h | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h new file mode 100644 index 0000000..ca09a16 --- /dev/null +++ b/src/include/portability/instr_time.h @@ -0,0 +1,256 @@ +/*------------------------------------------------------------------------- + * + * instr_time.h + * portable high-precision interval timing + * + * This file provides an abstraction layer to hide portability issues in + * interval timing. On Unix we use clock_gettime() if available, else + * gettimeofday(). On Windows, gettimeofday() gives a low-precision result + * so we must use QueryPerformanceCounter() instead. These macros also give + * some breathing room to use other high-precision-timing APIs. + * + * The basic data type is instr_time, which all callers should treat as an + * opaque typedef. instr_time can store either an absolute time (of + * unspecified reference time) or an interval. The operations provided + * for it are: + * + * INSTR_TIME_IS_ZERO(t) is t equal to zero? + * + * INSTR_TIME_SET_ZERO(t) set t to zero (memset is acceptable too) + * + * INSTR_TIME_SET_CURRENT(t) set t to current time + * + * INSTR_TIME_SET_CURRENT_LAZY(t) set t to current time if t is zero, + * evaluates to whether t changed + * + * INSTR_TIME_ADD(x, y) x += y + * + * INSTR_TIME_SUBTRACT(x, y) x -= y + * + * INSTR_TIME_ACCUM_DIFF(x, y, z) x += (y - z) + * + * INSTR_TIME_GET_DOUBLE(t) convert t to double (in seconds) + * + * INSTR_TIME_GET_MILLISEC(t) convert t to double (in milliseconds) + * + * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) + * + * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert + * absolute times to intervals. The INSTR_TIME_GET_xxx operations are + * only useful on intervals. + * + * When summing multiple measurements, it's recommended to leave the + * running sum in instr_time form (ie, use INSTR_TIME_ADD or + * INSTR_TIME_ACCUM_DIFF) and convert to a result format only at the end. + * + * Beware of multiple evaluations of the macro arguments. + * + * + * Copyright (c) 2001-2022, PostgreSQL Global Development Group + * + * src/include/portability/instr_time.h + * + *------------------------------------------------------------------------- + */ +#ifndef INSTR_TIME_H +#define INSTR_TIME_H + +#ifndef WIN32 + +#ifdef HAVE_CLOCK_GETTIME + +/* Use clock_gettime() */ + +#include <time.h> + +/* + * The best clockid to use according to the POSIX spec is CLOCK_MONOTONIC, + * since that will give reliable interval timing even in the face of changes + * to the system clock. However, POSIX doesn't require implementations to + * provide anything except CLOCK_REALTIME, so fall back to that if we don't + * find CLOCK_MONOTONIC. + * + * Also, some implementations have nonstandard clockids with better properties + * than CLOCK_MONOTONIC. In particular, as of macOS 10.12, Apple provides + * CLOCK_MONOTONIC_RAW which is both faster to read and higher resolution than + * their version of CLOCK_MONOTONIC. + */ +#if defined(__darwin__) && defined(CLOCK_MONOTONIC_RAW) +#define PG_INSTR_CLOCK CLOCK_MONOTONIC_RAW +#elif defined(CLOCK_MONOTONIC) +#define PG_INSTR_CLOCK CLOCK_MONOTONIC +#else +#define PG_INSTR_CLOCK CLOCK_REALTIME +#endif + +typedef struct timespec instr_time; + +#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + +#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + +#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) + +#define INSTR_TIME_ADD(x,y) \ + do { \ + (x).tv_sec += (y).tv_sec; \ + (x).tv_nsec += (y).tv_nsec; \ + /* Normalize */ \ + while ((x).tv_nsec >= 1000000000) \ + { \ + (x).tv_nsec -= 1000000000; \ + (x).tv_sec++; \ + } \ + } while (0) + +#define INSTR_TIME_SUBTRACT(x,y) \ + do { \ + (x).tv_sec -= (y).tv_sec; \ + (x).tv_nsec -= (y).tv_nsec; \ + /* Normalize */ \ + while ((x).tv_nsec < 0) \ + { \ + (x).tv_nsec += 1000000000; \ + (x).tv_sec--; \ + } \ + } while (0) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + do { \ + (x).tv_sec += (y).tv_sec - (z).tv_sec; \ + (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ + /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ + while ((x).tv_nsec < 0) \ + { \ + (x).tv_nsec += 1000000000; \ + (x).tv_sec--; \ + } \ + while ((x).tv_nsec >= 1000000000) \ + { \ + (x).tv_nsec -= 1000000000; \ + (x).tv_sec++; \ + } \ + } while (0) + +#define INSTR_TIME_GET_DOUBLE(t) \ + (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) + +#define INSTR_TIME_GET_MILLISEC(t) \ + (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) + +#else /* !HAVE_CLOCK_GETTIME */ + +/* Use gettimeofday() */ + +#include <sys/time.h> + +typedef struct timeval instr_time; + +#define INSTR_TIME_IS_ZERO(t) ((t).tv_usec == 0 && (t).tv_sec == 0) + +#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_usec = 0) + +#define INSTR_TIME_SET_CURRENT(t) gettimeofday(&(t), NULL) + +#define INSTR_TIME_ADD(x,y) \ + do { \ + (x).tv_sec += (y).tv_sec; \ + (x).tv_usec += (y).tv_usec; \ + /* Normalize */ \ + while ((x).tv_usec >= 1000000) \ + { \ + (x).tv_usec -= 1000000; \ + (x).tv_sec++; \ + } \ + } while (0) + +#define INSTR_TIME_SUBTRACT(x,y) \ + do { \ + (x).tv_sec -= (y).tv_sec; \ + (x).tv_usec -= (y).tv_usec; \ + /* Normalize */ \ + while ((x).tv_usec < 0) \ + { \ + (x).tv_usec += 1000000; \ + (x).tv_sec--; \ + } \ + } while (0) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + do { \ + (x).tv_sec += (y).tv_sec - (z).tv_sec; \ + (x).tv_usec += (y).tv_usec - (z).tv_usec; \ + /* Normalize after each add to avoid overflow/underflow of tv_usec */ \ + while ((x).tv_usec < 0) \ + { \ + (x).tv_usec += 1000000; \ + (x).tv_sec--; \ + } \ + while ((x).tv_usec >= 1000000) \ + { \ + (x).tv_usec -= 1000000; \ + (x).tv_sec++; \ + } \ + } while (0) + +#define INSTR_TIME_GET_DOUBLE(t) \ + (((double) (t).tv_sec) + ((double) (t).tv_usec) / 1000000.0) + +#define INSTR_TIME_GET_MILLISEC(t) \ + (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_usec) / 1000.0) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) (t).tv_usec) + +#endif /* HAVE_CLOCK_GETTIME */ + +#else /* WIN32 */ + +/* Use QueryPerformanceCounter() */ + +typedef LARGE_INTEGER instr_time; + +#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + +#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) + +#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) + +#define INSTR_TIME_ADD(x,y) \ + ((x).QuadPart += (y).QuadPart) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).QuadPart -= (y).QuadPart) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).QuadPart += (y).QuadPart - (z).QuadPart) + +#define INSTR_TIME_GET_DOUBLE(t) \ + (((double) (t).QuadPart) / GetTimerFrequency()) + +#define INSTR_TIME_GET_MILLISEC(t) \ + (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) + +#define INSTR_TIME_GET_MICROSEC(t) \ + ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + +static inline double +GetTimerFrequency(void) +{ + LARGE_INTEGER f; + + QueryPerformanceFrequency(&f); + return (double) f.QuadPart; +} + +#endif /* WIN32 */ + +/* same macro on all platforms */ + +#define INSTR_TIME_SET_CURRENT_LAZY(t) \ + (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#endif /* INSTR_TIME_H */ |