agora inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
261+ messages / 2 participants
[nested] [flat]

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)

Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.

Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.

On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.

Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.

To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.

Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
 src/include/portability/instr_time.h | 162 ++++++++++++++-------------
 1 file changed, 86 insertions(+), 76 deletions(-)

diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
  *
  * INSTR_TIME_GET_MICROSEC(t)		convert t to uint64 (in microseconds)
  *
+ * INSTR_TIME_GET_NANOSEC(t)		convert t to uint64 (in nanoseconds)
+ *
  * 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.
@@ -54,8 +56,32 @@
 #ifndef INSTR_TIME_H
 #define INSTR_TIME_H
 
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+	int64		ticks;			/* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S	INT64CONST(1000000000)
+#define NS_PER_MS	INT64CONST(1000000)
+#define NS_PER_US	INT64CONST(1000)
+
+
 #ifndef WIN32
 
+
 /* Use clock_gettime() */
 
 #include <time.h>
@@ -80,93 +106,43 @@
 #define PG_INSTR_CLOCK	CLOCK_REALTIME
 #endif
 
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+	instr_time	now;
+	struct timespec tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).tv_nsec == 0 && (t).tv_sec == 0)
+	clock_gettime(PG_INSTR_CLOCK, &tmp);
+	now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
 
-#define INSTR_TIME_SET_ZERO(t)	((t).tv_sec = 0, (t).tv_nsec = 0)
+	return now;
+}
 
-#define INSTR_TIME_SET_CURRENT(t)	((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_clock_gettime_ns())
 
-#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_GET_NANOSEC(t) \
+	((int64) (t).ticks)
 
-#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							/* WIN32 */
 
+
 /* Use QueryPerformanceCounter() */
 
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+	instr_time	now;
+	LARGE_INTEGER tmp;
 
-#define INSTR_TIME_IS_ZERO(t)	((t).QuadPart == 0)
+	QueryPerformanceCounter(&tmp);
+	now.ticks = tmp.QuadPart;
 
-#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()))
+	return now;
+}
 
 static inline double
 GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
 	return (double) f.QuadPart;
 }
 
+#define INSTR_TIME_SET_CURRENT(t) \
+	((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+	((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
 #endif							/* WIN32 */
 
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t)	((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t)	((t).ticks = 0)
 
 #define INSTR_TIME_SET_CURRENT_LAZY(t) \
 	(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
 
+
+#define INSTR_TIME_ADD(x,y) \
+	((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+	((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+	((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+	((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+	(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
 #endif							/* INSTR_TIME_H */
-- 
2.38.0


--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"



^ permalink  raw  reply  [nested|flat] 261+ messages in thread

* [PATCH v3 1/4] pg_dump/pg_dumpall: bump minimum supported version to v10
@ 2026-04-17 17:15 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 261+ messages in thread

From: Nathan Bossart @ 2026-04-17 17:15 UTC (permalink / raw)

---
 doc/src/sgml/ref/pg_dump.sgml |  14 +-
 doc/src/sgml/runtime.sgml     |   2 +-
 src/bin/pg_dump/pg_dump.c     | 330 ++--------------------------------
 src/bin/pg_dump/pg_dumpall.c  |  32 +---
 4 files changed, 22 insertions(+), 356 deletions(-)

diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index ae1bc14d2f2..64a3447e445 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -407,18 +407,6 @@ PostgreSQL documentation
         and there is no way to continue with the dump, so <application>pg_dump</application> has no choice
         but to abort the dump.
        </para>
-       <para>
-        To perform a parallel dump, the database server needs to support
-        synchronized snapshots, a feature that was introduced in
-        <productname>PostgreSQL</productname> 9.2 for primary servers and 10
-        for standbys. With this feature, database clients can ensure they see
-        the same data set even though they use different connections.
-        <command>pg_dump -j</command> uses multiple database connections; it
-        connects to the database once with the leader process and once again
-        for each worker job. Without the synchronized snapshot feature, the
-        different worker jobs wouldn't be guaranteed to see the same data in
-        each connection, which could lead to an inconsistent backup.
-       </para>
       </listitem>
      </varlistentry>
 
@@ -1718,7 +1706,7 @@ CREATE DATABASE foo WITH TEMPLATE template0;
    <productname>PostgreSQL</productname> server versions newer than
    <application>pg_dump</application>'s version.  <application>pg_dump</application> can also
    dump from <productname>PostgreSQL</productname> servers older than its own version.
-   (Currently, servers back to version 9.2 are supported.)
+   (Currently, servers back to version 10 are supported.)
    However, <application>pg_dump</application> cannot dump from
    <productname>PostgreSQL</productname> servers newer than its own major version;
    it will refuse to even try, rather than risk making an invalid dump.
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index dfa292c2c3a..d9984910cc4 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1776,7 +1776,7 @@ $ <userinput>kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid`</userinput
     version of
     <productname>PostgreSQL</productname>, to take advantage of enhancements
     that might have been made in these programs.  Current releases of the
-    dump programs can read data from any server version back to 9.2.
+    dump programs can read data from any server version back to 10.
    </para>
 
    <para>
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index d56dcc701ce..eed9aaeb7c1 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -977,10 +977,10 @@ main(int argc, char **argv)
 
 
 	/*
-	 * We allow the server to be back to 9.2, and up to any minor release of
+	 * We allow the server to be back to 10, and up to any minor release of
 	 * our own major version.  (See also version check in pg_dumpall.c.)
 	 */
-	fout->minRemoteVersion = 90200;
+	fout->minRemoteVersion = 100000;
 	fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
 
 	fout->numWorkers = numWorkers;
@@ -1491,9 +1491,7 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	 * Disable timeouts if supported.
 	 */
 	ExecuteSqlStatement(AH, "SET statement_timeout = 0");
-	if (AH->remoteVersion >= 90300)
 		ExecuteSqlStatement(AH, "SET lock_timeout = 0");
-	if (AH->remoteVersion >= 90600)
 		ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
 	if (AH->remoteVersion >= 170000)
 		ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
@@ -1507,13 +1505,10 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	/*
 	 * Adjust row-security mode, if supported.
 	 */
-	if (AH->remoteVersion >= 90500)
-	{
 		if (dopt->enable_row_security)
 			ExecuteSqlStatement(AH, "SET row_security = on");
 		else
 			ExecuteSqlStatement(AH, "SET row_security = off");
-	}
 
 	/*
 	 * For security reasons, we restrict the expansion of non-system views and
@@ -1568,11 +1563,7 @@ setup_connection(Archive *AH, const char *dumpencoding,
 		destroyPQExpBuffer(query);
 	}
 	else if (AH->numWorkers > 1)
-	{
-		if (AH->isStandby && AH->remoteVersion < 100000)
-			pg_fatal("parallel dumps from standby servers are not supported by this server version");
 		AH->sync_snapshot_id = get_synchronized_snapshot(AH);
-	}
 }
 
 /* Set up connection for a parallel worker process */
@@ -1964,12 +1955,7 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
 	if (fout->dopt->binary_upgrade)
 		dobj->dump = ext->dobj.dump;
 	else
-	{
-		if (fout->remoteVersion < 90600)
-			dobj->dump = DUMP_COMPONENT_NONE;
-		else
 			dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
-	}
 
 	return true;
 }
@@ -2000,11 +1986,10 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
 			simple_oid_list_member(&schema_include_oids,
 								   nsinfo->dobj.catId.oid) ?
 			DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
-	else if (fout->remoteVersion >= 90600 &&
-			 strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
+	else if (strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
 	{
 		/*
-		 * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
+		 * We dump out any ACLs defined in pg_catalog, if
 		 * they are interesting (and not the original ACLs which were set at
 		 * initdb time, see pg_init_privs).
 		 */
@@ -2213,8 +2198,7 @@ selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
 	else
 	{
 		if (plang->dobj.catId.oid <= g_last_builtin_oid)
-			plang->dobj.dump = fout->remoteVersion < 90600 ?
-				DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
+			plang->dobj.dump = DUMP_COMPONENT_ACL;
 		else
 			plang->dobj.dump = DUMP_COMPONENT_ALL;
 	}
@@ -2231,13 +2215,6 @@ selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
 static void
 selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
 {
-	/* see getAccessMethods() comment about v9.6. */
-	if (fout->remoteVersion < 90600)
-	{
-		method->dobj.dump = DUMP_COMPONENT_NONE;
-		return;
-	}
-
 	if (checkExtensionMembership(&method->dobj, fout))
 		return;					/* extension membership overrides all else */
 
@@ -3123,10 +3100,6 @@ buildMatViewRefreshDependencies(Archive *fout)
 				i_objid,
 				i_refobjid;
 
-	/* No Mat Views before 9.3. */
-	if (fout->remoteVersion < 90300)
-		return;
-
 	query = createPQExpBuffer();
 
 	appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
@@ -3325,10 +3298,7 @@ dumpDatabase(Archive *fout)
 						 "datcollate, datctype, datfrozenxid, "
 						 "datacl, acldefault('d', datdba) AS acldefault, "
 						 "datistemplate, datconnlimit, ");
-	if (fout->remoteVersion >= 90300)
 		appendPQExpBufferStr(dbQry, "datminmxid, ");
-	else
-		appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
 	if (fout->remoteVersion >= 170000)
 		appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
 	else if (fout->remoteVersion >= 150000)
@@ -3670,17 +3640,11 @@ dumpDatabase(Archive *fout)
 					ii_oid,
 					ii_relminmxid;
 
-		if (fout->remoteVersion >= 90300)
 			appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
 							  "FROM pg_catalog.pg_class\n"
 							  "WHERE oid IN (%u, %u, %u, %u);\n",
 							  LargeObjectRelationId, LargeObjectLOidPNIndexId,
 							  LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
-		else
-			appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
-							  "FROM pg_catalog.pg_class\n"
-							  "WHERE oid IN (%u, %u);\n",
-							  LargeObjectRelationId, LargeObjectLOidPNIndexId);
 
 		lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
 
@@ -4243,10 +4207,6 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 				j,
 				ntups;
 
-	/* No policies before 9.5 */
-	if (fout->remoteVersion < 90500)
-		return;
-
 	/* Skip if --no-policies was specified */
 	if (dopt->no_policies)
 		return;
@@ -4316,10 +4276,7 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
 
 	printfPQExpBuffer(query,
 					  "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
-	if (fout->remoteVersion >= 100000)
 		appendPQExpBufferStr(query, "pol.polpermissive, ");
-	else
-		appendPQExpBufferStr(query, "'t' as polpermissive, ");
 	appendPQExpBuffer(query,
 					  "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
 					  "   pg_catalog.array_to_string(ARRAY(SELECT pg_catalog.quote_ident(rolname) from pg_catalog.pg_roles WHERE oid = ANY(pol.polroles)), ', ') END AS polroles, "
@@ -4534,7 +4491,7 @@ getPublications(Archive *fout)
 	int			i,
 				ntups;
 
-	if (dopt->no_publications || fout->remoteVersion < 100000)
+	if (dopt->no_publications)
 		return;
 
 	query = createPQExpBuffer();
@@ -4897,7 +4854,7 @@ getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
 				j,
 				ntups;
 
-	if (dopt->no_publications || fout->remoteVersion < 100000)
+	if (dopt->no_publications)
 		return;
 
 	query = createPQExpBuffer();
@@ -5187,7 +5144,7 @@ getSubscriptions(Archive *fout)
 	int			i,
 				ntups;
 
-	if (dopt->no_subscriptions || fout->remoteVersion < 100000)
+	if (dopt->no_subscriptions)
 		return;
 
 	if (!is_superuser(fout))
@@ -6675,24 +6632,12 @@ getAccessMethods(Archive *fout)
 	query = createPQExpBuffer();
 
 	/*
-	 * Select all access methods from pg_am table.  v9.6 introduced CREATE
-	 * ACCESS METHOD, so earlier versions usually have only built-in access
-	 * methods.  v9.6 also changed the access method API, replacing dozens of
-	 * pg_am columns with amhandler.  Even if a user created an access method
-	 * by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
-	 * columns to a v9.6+ CREATE ACCESS METHOD.  Hence, before v9.6, read
-	 * pg_am just to facilitate findAccessMethodByOid() providing the
-	 * OID-to-name mapping.
+	 * Select all access methods from pg_am table.
 	 */
 	appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
-	if (fout->remoteVersion >= 90600)
 		appendPQExpBufferStr(query,
 							 "amtype, "
 							 "amhandler::pg_catalog.regproc AS amhandler ");
-	else
-		appendPQExpBufferStr(query,
-							 "'i'::pg_catalog.\"char\" AS amtype, "
-							 "'-'::pg_catalog.regproc AS amhandler ");
 	appendPQExpBufferStr(query, "FROM pg_am");
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -6878,15 +6823,12 @@ getAggregates(Archive *fout)
 	int			i_proowner;
 	int			i_aggacl;
 	int			i_acldefault;
+	const char *agg_check;
 
 	/*
 	 * Find all interesting aggregates.  See comment in getFuncs() for the
 	 * rationale behind the filtering logic.
 	 */
-	if (fout->remoteVersion >= 90600)
-	{
-		const char *agg_check;
-
 		agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
 					 : "p.proisagg");
 
@@ -6916,29 +6858,6 @@ getAggregates(Archive *fout)
 								 "refclassid = 'pg_extension'::regclass AND "
 								 "deptype = 'e')");
 		appendPQExpBufferChar(query, ')');
-	}
-	else
-	{
-		appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
-							 "pronamespace AS aggnamespace, "
-							 "pronargs, proargtypes, "
-							 "proowner, "
-							 "proacl AS aggacl, "
-							 "acldefault('f', proowner) AS acldefault "
-							 "FROM pg_proc p "
-							 "WHERE proisagg AND ("
-							 "pronamespace != "
-							 "(SELECT oid FROM pg_namespace "
-							 "WHERE nspname = 'pg_catalog')");
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
-		appendPQExpBufferChar(query, ')');
-	}
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -7021,6 +6940,7 @@ getFuncs(Archive *fout)
 	int			i_prorettype;
 	int			i_proacl;
 	int			i_acldefault;
+	const char *not_agg_check;
 
 	/*
 	 * Find all interesting functions.  This is a bit complicated:
@@ -7039,14 +6959,10 @@ getFuncs(Archive *fout)
 	 * include them, since we want to dump extension members individually in
 	 * that mode.  Also, if they are used by casts or transforms then we need
 	 * to gather the information about them, though they won't be dumped if
-	 * they are built-in.  Also, in 9.6 and up, include functions in
+	 * they are built-in.  Also, include functions in
 	 * pg_catalog if they have an ACL different from what's shown in
 	 * pg_init_privs (so we have to join to pg_init_privs; annoying).
 	 */
-	if (fout->remoteVersion >= 90600)
-	{
-		const char *not_agg_check;
-
 		not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
 						 : "NOT p.proisagg");
 
@@ -7090,46 +7006,6 @@ getFuncs(Archive *fout)
 		appendPQExpBufferStr(query,
 							 "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
 		appendPQExpBufferChar(query, ')');
-	}
-	else
-	{
-		appendPQExpBuffer(query,
-						  "SELECT tableoid, oid, proname, prolang, "
-						  "pronargs, proargtypes, prorettype, proacl, "
-						  "acldefault('f', proowner) AS acldefault, "
-						  "pronamespace, "
-						  "proowner "
-						  "FROM pg_proc p "
-						  "WHERE NOT proisagg"
-						  "\n  AND NOT EXISTS (SELECT 1 FROM pg_depend "
-						  "WHERE classid = 'pg_proc'::regclass AND "
-						  "objid = p.oid AND deptype = 'i')"
-						  "\n  AND ("
-						  "\n  pronamespace != "
-						  "(SELECT oid FROM pg_namespace "
-						  "WHERE nspname = 'pg_catalog')"
-						  "\n  OR EXISTS (SELECT 1 FROM pg_cast"
-						  "\n  WHERE pg_cast.oid > '%u'::oid"
-						  "\n  AND p.oid = pg_cast.castfunc)",
-						  g_last_builtin_oid);
-
-		if (fout->remoteVersion >= 90500)
-			appendPQExpBuffer(query,
-							  "\n  OR EXISTS (SELECT 1 FROM pg_transform"
-							  "\n  WHERE pg_transform.oid > '%u'::oid"
-							  "\n  AND (p.oid = pg_transform.trffromsql"
-							  "\n  OR p.oid = pg_transform.trftosql))",
-							  g_last_builtin_oid);
-
-		if (dopt->binary_upgrade)
-			appendPQExpBufferStr(query,
-								 "\n  OR EXISTS(SELECT 1 FROM pg_depend WHERE "
-								 "classid = 'pg_proc'::regclass AND "
-								 "objid = p.oid AND "
-								 "refclassid = 'pg_extension'::regclass AND "
-								 "deptype = 'e')");
-		appendPQExpBufferChar(query, ')');
-	}
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -7378,64 +7254,31 @@ getTables(Archive *fout, int *numTables)
 		appendPQExpBufferStr(query,
 							 "c.relhasoids, ");
 
-	if (fout->remoteVersion >= 90300)
 		appendPQExpBufferStr(query,
 							 "c.relispopulated, ");
-	else
-		appendPQExpBufferStr(query,
-							 "'t' as relispopulated, ");
 
-	if (fout->remoteVersion >= 90400)
 		appendPQExpBufferStr(query,
 							 "c.relreplident, ");
-	else
-		appendPQExpBufferStr(query,
-							 "'d' AS relreplident, ");
 
-	if (fout->remoteVersion >= 90500)
 		appendPQExpBufferStr(query,
 							 "c.relrowsecurity, c.relforcerowsecurity, ");
-	else
-		appendPQExpBufferStr(query,
-							 "false AS relrowsecurity, "
-							 "false AS relforcerowsecurity, ");
 
-	if (fout->remoteVersion >= 90300)
 		appendPQExpBufferStr(query,
 							 "c.relminmxid, tc.relminmxid AS tminmxid, ");
-	else
-		appendPQExpBufferStr(query,
-							 "0 AS relminmxid, 0 AS tminmxid, ");
 
-	if (fout->remoteVersion >= 90300)
 		appendPQExpBufferStr(query,
 							 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
 							 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
 							 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
-	else
-		appendPQExpBufferStr(query,
-							 "c.reloptions, NULL AS checkoption, ");
 
-	if (fout->remoteVersion >= 90600)
 		appendPQExpBufferStr(query,
 							 "am.amname, ");
-	else
-		appendPQExpBufferStr(query,
-							 "NULL AS amname, ");
 
-	if (fout->remoteVersion >= 90600)
 		appendPQExpBufferStr(query,
 							 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
-	else
-		appendPQExpBufferStr(query,
-							 "false AS is_identity_sequence, ");
 
-	if (fout->remoteVersion >= 100000)
 		appendPQExpBufferStr(query,
 							 "c.relispartition AS ispartition ");
-	else
-		appendPQExpBufferStr(query,
-							 "false AS ispartition ");
 
 	/*
 	 * Left join to pg_depend to pick up dependency info linking sequences to
@@ -7453,9 +7296,8 @@ getTables(Archive *fout, int *numTables)
 						 "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
 
 	/*
-	 * In 9.6 and up, left join to pg_am to pick up the amname.
+	 * Left join to pg_am to pick up the amname.
 	 */
-	if (fout->remoteVersion >= 90600)
 		appendPQExpBufferStr(query,
 							 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
 
@@ -8028,12 +7870,8 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 						 "t.reloptions AS indreloptions, ");
 
 
-	if (fout->remoteVersion >= 90400)
 		appendPQExpBufferStr(query,
 							 "i.indisreplident, ");
-	else
-		appendPQExpBufferStr(query,
-							 "false AS indisreplident, ");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(query,
@@ -8318,10 +8156,6 @@ getExtendedStatistics(Archive *fout)
 	int			i_stattarget;
 	int			i;
 
-	/* Extended statistics were new in v10 */
-	if (fout->remoteVersion < 100000)
-		return;
-
 	query = createPQExpBuffer();
 
 	if (fout->remoteVersion < 130000)
@@ -8988,10 +8822,6 @@ getEventTriggers(Archive *fout)
 				i_evtenabled;
 	int			ntups;
 
-	/* Before 9.3, there are no event triggers */
-	if (fout->remoteVersion < 90300)
-		return;
-
 	query = createPQExpBuffer();
 
 	appendPQExpBufferStr(query,
@@ -9258,10 +9088,6 @@ getTransforms(Archive *fout)
 	int			i_trffromsql;
 	int			i_trftosql;
 
-	/* Transforms didn't exist pre-9.5 */
-	if (fout->remoteVersion < 90500)
-		return;
-
 	query = createPQExpBuffer();
 
 	appendPQExpBufferStr(query, "SELECT tableoid, oid, "
@@ -9489,12 +9315,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 		appendPQExpBufferStr(q,
 							 "'' AS attcompression,\n");
 
-	if (fout->remoteVersion >= 100000)
 		appendPQExpBufferStr(q,
 							 "a.attidentity,\n");
-	else
-		appendPQExpBufferStr(q,
-							 "'' AS attidentity,\n");
 
 	if (fout->remoteVersion >= 110000)
 		appendPQExpBufferStr(q,
@@ -10897,8 +10719,6 @@ getAdditionalACLs(Archive *fout)
 	PQclear(res);
 
 	/* Fetch initial-privileges data */
-	if (fout->remoteVersion >= 90600)
-	{
 		printfPQExpBuffer(query,
 						  "SELECT objoid, classoid, objsubid, privtype, initprivs "
 						  "FROM pg_init_privs");
@@ -10966,7 +10786,6 @@ getAdditionalACLs(Archive *fout)
 			}
 		}
 		PQclear(res);
-	}
 
 	destroyPQExpBuffer(query);
 }
@@ -11141,15 +10960,6 @@ fetchAttributeStats(Archive *fout)
 	static bool restarted;
 	int			max_rels = MAX_ATTR_STATS_RELS;
 
-	/*
-	 * Our query for retrieving statistics for multiple relations uses WITH
-	 * ORDINALITY and multi-argument UNNEST(), both of which were introduced
-	 * in v9.4.  For older versions, we resort to gathering statistics for a
-	 * single relation at a time.
-	 */
-	if (fout->remoteVersion < 90400)
-		max_rels = 1;
-
 	/* If we're just starting, set our TOC pointer. */
 	if (!te)
 		te = AH->toc->next;
@@ -11320,16 +11130,11 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 		 * The results must be in the order of the relations supplied in the
 		 * parameters to ensure we remain in sync as we walk through the TOC.
 		 *
-		 * For v9.4 through v18, the redundant filter clause on s.tablename =
+		 * For versions before 19, the redundant filter clause on s.tablename =
 		 * ANY(...) seems sufficient to convince the planner to use
 		 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
 		 * In newer versions, pg_stats returns the table OIDs, eliminating the
 		 * need for that hack.
-		 *
-		 * Our query for retrieving statistics for multiple relations uses
-		 * WITH ORDINALITY and multi-argument UNNEST(), both of which were
-		 * introduced in v9.4.  For older versions, we resort to gathering
-		 * statistics for a single relation at a time.
 		 */
 		if (fout->remoteVersion >= 190000)
 			appendPQExpBufferStr(query,
@@ -11337,7 +11142,7 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 								 "JOIN unnest($1) WITH ORDINALITY AS u (tableid, ord) "
 								 "ON s.tableid = u.tableid "
 								 "ORDER BY u.ord, s.attname, s.inherited");
-		else if (fout->remoteVersion >= 90400)
+		else
 			appendPQExpBufferStr(query,
 								 "FROM pg_catalog.pg_stats s "
 								 "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
@@ -11345,12 +11150,6 @@ dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
 								 "AND s.tablename = u.tablename "
 								 "WHERE s.tablename = ANY($2) "
 								 "ORDER BY u.ord, s.attname, s.inherited");
-		else
-			appendPQExpBufferStr(query,
-								 "FROM pg_catalog.pg_stats s "
-								 "WHERE s.schemaname = $1[1] "
-								 "AND s.tablename = $2[1] "
-								 "ORDER BY s.attname, s.inherited");
 
 		ExecuteSqlStatement(fout, query->data);
 
@@ -13672,19 +13471,11 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
 							 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
 							 "proleakproof,\n");
 
-		if (fout->remoteVersion >= 90500)
 			appendPQExpBufferStr(query,
 								 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
-		else
-			appendPQExpBufferStr(query,
-								 "NULL AS protrftypes,\n");
 
-		if (fout->remoteVersion >= 90600)
 			appendPQExpBufferStr(query,
 								 "proparallel,\n");
-		else
-			appendPQExpBufferStr(query,
-								 "'u' AS proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -15174,14 +14965,9 @@ dumpCollation(Archive *fout, const CollInfo *collinfo)
 	/* Get collation-specific details */
 	appendPQExpBufferStr(query, "SELECT ");
 
-	if (fout->remoteVersion >= 100000)
 		appendPQExpBufferStr(query,
 							 "collprovider, "
 							 "collversion, ");
-	else
-		appendPQExpBufferStr(query,
-							 "'c' AS collprovider, "
-							 "NULL AS collversion, ");
 
 	if (fout->remoteVersion >= 120000)
 		appendPQExpBufferStr(query,
@@ -15588,7 +15374,6 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 							 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
 							 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
 
-		if (fout->remoteVersion >= 90400)
 			appendPQExpBufferStr(query,
 								 "aggkind,\n"
 								 "aggmtransfn,\n"
@@ -15600,31 +15385,12 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 								 "aggtransspace,\n"
 								 "aggmtransspace,\n"
 								 "aggminitval,\n");
-		else
-			appendPQExpBufferStr(query,
-								 "'n' AS aggkind,\n"
-								 "'-' AS aggmtransfn,\n"
-								 "'-' AS aggminvtransfn,\n"
-								 "'-' AS aggmfinalfn,\n"
-								 "0 AS aggmtranstype,\n"
-								 "false AS aggfinalextra,\n"
-								 "false AS aggmfinalextra,\n"
-								 "0 AS aggtransspace,\n"
-								 "0 AS aggmtransspace,\n"
-								 "NULL AS aggminitval,\n");
 
-		if (fout->remoteVersion >= 90600)
 			appendPQExpBufferStr(query,
 								 "aggcombinefn,\n"
 								 "aggserialfn,\n"
 								 "aggdeserialfn,\n"
 								 "proparallel,\n");
-		else
-			appendPQExpBufferStr(query,
-								 "'-' AS aggcombinefn,\n"
-								 "'-' AS aggserialfn,\n"
-								 "'-' AS aggdeserialfn,\n"
-								 "'u' AS proparallel,\n");
 
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
@@ -17084,8 +16850,6 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
 			appendPQExpBufferStr(query,
 								 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
 
-			if (fout->remoteVersion >= 90600)
-			{
 				/*
 				 * In principle we should call acldefault('c', relowner) to
 				 * get the default ACL for a column.  However, we don't
@@ -17110,17 +16874,6 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
 									 "NOT at.attisdropped "
 									 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
 									 "ORDER BY at.attnum");
-			}
-			else
-			{
-				appendPQExpBufferStr(query,
-									 "SELECT attname, attacl, '{}' AS acldefault, "
-									 "NULL AS privtype, NULL AS initprivs "
-									 "FROM pg_catalog.pg_attribute "
-									 "WHERE attrelid = $1 AND NOT attisdropped "
-									 "AND attacl IS NOT NULL "
-									 "ORDER BY attnum");
-			}
 
 			ExecuteSqlStatement(fout, query->data);
 
@@ -19410,16 +19163,10 @@ collectSequences(Archive *fout)
 	const char *query;
 
 	/*
-	 * Before Postgres 10, sequence metadata is in the sequence itself.  With
-	 * some extra effort, we might be able to use the sorted table for those
-	 * versions, but for now it seems unlikely to be worth it.
-	 *
 	 * Since version 18, we can gather the sequence data in this query with
 	 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
 	 */
-	if (fout->remoteVersion < 100000)
-		return;
-	else if (fout->remoteVersion < 180000 ||
+	if (fout->remoteVersion < 180000 ||
 			 (!fout->dopt->dumpData && !fout->dopt->sequence_data))
 		query = "SELECT seqrelid, format_type(seqtypid, NULL), "
 			"seqstart, seqincrement, "
@@ -19477,59 +19224,20 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 	PQExpBuffer delqry = createPQExpBuffer();
 	char	   *qseqname;
 	TableInfo  *owning_tab = NULL;
+	SequenceItem key = {0};
 
 	qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
 
 	/*
-	 * For versions >= 10, the sequence information is gathered in a sorted
+	 * The sequence information is gathered in a sorted
 	 * table before any calls to dumpSequence().  See collectSequences() for
 	 * more information.
 	 */
-	if (fout->remoteVersion >= 100000)
-	{
-		SequenceItem key = {0};
-
 		Assert(sequences);
 
 		key.oid = tbinfo->dobj.catId.oid;
 		seq = bsearch(&key, sequences, nsequences,
 					  sizeof(SequenceItem), SequenceItemCmp);
-	}
-	else
-	{
-		PGresult   *res;
-
-		/*
-		 * Before PostgreSQL 10, sequence metadata is in the sequence itself.
-		 *
-		 * Note: it might seem that 'bigint' potentially needs to be
-		 * schema-qualified, but actually that's a keyword.
-		 */
-		appendPQExpBuffer(query,
-						  "SELECT 'bigint' AS sequence_type, "
-						  "start_value, increment_by, max_value, min_value, "
-						  "cache_value, is_cycled FROM %s",
-						  fmtQualifiedDumpable(tbinfo));
-
-		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
-
-		if (PQntuples(res) != 1)
-			pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
-							  "query to get data of sequence \"%s\" returned %d rows (expected 1)",
-							  PQntuples(res)),
-					 tbinfo->dobj.name, PQntuples(res));
-
-		seq = pg_malloc0_object(SequenceItem);
-		seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
-		seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
-		seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
-		seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
-		seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
-		seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
-		seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
-
-		PQclear(res);
-	}
 
 	/* Calculate default limits for a sequence of this type */
 	is_ascending = (seq->incby >= 0);
@@ -19708,8 +19416,6 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 					 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
 					 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
 
-	if (fout->remoteVersion < 100000)
-		pg_free(seq);
 	destroyPQExpBuffer(query);
 	destroyPQExpBuffer(delqry);
 	free(qseqname);
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index c1f43113c53..5b10f7122b7 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -693,11 +693,11 @@ main(int argc, char *argv[])
 		fout->verbose = verbose;
 
 		/*
-		 * We allow the server to be back to 9.2, and up to any minor release
+		 * We allow the server to be back to 10, and up to any minor release
 		 * of our own major version.  (See also version check in
 		 * pg_dumpall.c.)
 		 */
-		fout->minRemoteVersion = 90200;
+		fout->minRemoteVersion = 100000;
 		fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
 		fout->numWorkers = 1;
 
@@ -954,17 +954,11 @@ dropRoles(PGconn *conn)
 	int			i_rolname;
 	int			i;
 
-	if (server_version >= 90600)
 		printfPQExpBuffer(buf,
 						  "SELECT rolname "
 						  "FROM %s "
 						  "WHERE rolname !~ '^pg_' "
 						  "ORDER BY 1", role_catalog);
-	else
-		printfPQExpBuffer(buf,
-						  "SELECT rolname "
-						  "FROM %s "
-						  "ORDER BY 1", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
@@ -1041,7 +1035,6 @@ dumpRoles(PGconn *conn)
 	 * Notes: rolconfig is dumped later, and pg_authid must be used for
 	 * extracting rolcomment regardless of role_catalog.
 	 */
-	if (server_version >= 90600)
 		printfPQExpBuffer(buf,
 						  "SELECT oid, rolname, rolsuper, rolinherit, "
 						  "rolcreaterole, rolcreatedb, "
@@ -1052,27 +1045,6 @@ dumpRoles(PGconn *conn)
 						  "FROM %s "
 						  "WHERE rolname !~ '^pg_' "
 						  "ORDER BY 2", role_catalog);
-	else if (server_version >= 90500)
-		printfPQExpBuffer(buf,
-						  "SELECT oid, rolname, rolsuper, rolinherit, "
-						  "rolcreaterole, rolcreatedb, "
-						  "rolcanlogin, rolconnlimit, rolpassword, "
-						  "rolvaliduntil, rolreplication, rolbypassrls, "
-						  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
-						  "rolname = current_user AS is_current_user "
-						  "FROM %s "
-						  "ORDER BY 2", role_catalog);
-	else
-		printfPQExpBuffer(buf,
-						  "SELECT oid, rolname, rolsuper, rolinherit, "
-						  "rolcreaterole, rolcreatedb, "
-						  "rolcanlogin, rolconnlimit, rolpassword, "
-						  "rolvaliduntil, rolreplication, "
-						  "false as rolbypassrls, "
-						  "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
-						  "rolname = current_user AS is_current_user "
-						  "FROM %s "
-						  "ORDER BY 2", role_catalog);
 
 	res = executeQuery(conn, buf->data);
 
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0002-pg_upgrade-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 261+ messages in thread


end of thread, other threads:[~2026-04-17 17:15 UTC | newest]

Thread overview: 261+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2026-04-17 17:15 [PATCH v3 1/4] pg_dump/pg_dumpall: bump minimum supported version to v10 Nathan Bossart <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox