summaryrefslogtreecommitdiffstats
path: root/src/include/portability/instr_time.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/include/portability/instr_time.h')
-rw-r--r--src/include/portability/instr_time.h256
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..d645932
--- /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-2020, 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 */