agora inbox for [email protected]
help / color / mirror / Atom feedRe: Re: Append with naive multiplexing of FDWs
261+ messages / 2 participants
[nested] [flat]
* Re: Re: Append with naive multiplexing of FDWs
@ 2020-01-14 09:12 [email protected] <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: [email protected] @ 2020-01-14 09:12 UTC (permalink / raw)
To: Horiguchi <[email protected]>; bruce <[email protected]>; ahsan.hadi <[email protected]>; +Cc: robertmhaas <[email protected]>; thomas.munro <[email protected]>; pgsql-hackers; sfrost <[email protected]>
Hello
I have tested the patch with a partition table with several foreign
partitions living on seperate data nodes. The initial testing was done
with a partition table having 3 foreign partitions, test was done with
variety of scale facters. The seonnd test was with fixed data per data
node but number of data nodes were increased incrementally to see
the peformance impact as more nodes are added to the cluster. The
test three is similar to the initial test but with much huge data and
4 nodes.
The results are summary is given below and test script attached:
Test ENV
Parent node:2Core 8G
Child Nodes:2Core 4G
Test one:
1.1 The partition struct as below:
[ ptf:(a int, b int, c varchar)]
(Parent node)
| | |
[ptf1] [ptf2] [ptf3]
(Node1) (Node2) (Node3)
The table data is partitioned across nodes, the test is done using a
simple select query and a count aggregate as shown below. The result
is an average of executing each query multiple times to ensure reliable
and consistent results.
①select * from ptf where b = 100;
②select count(*) from ptf;
1.2. Test Results
For ① result:
scalepernode master patched performance
2G 7s 2s 350%
5G 173s 63s 275%
10G 462s 156s 296%
20G 968s 327s 296%
30G 1472s 494s 297%
For ② result:
scalepernode master patched performance
2G 1079s 291s 370%
5G 2688s 741s 362%
10G 4473s 1493s 299%
It takes too long time to test a aggregate so the test was done with a
smaller data size.
1.3. summary
With the table partitioned over 3 nodes, the average performance gain
across variety of scale factors is almost 300%
Test Two
2.1 The partition struct as below:
[ ptf:(a int, b int, c varchar)]
(Parent node)
| | |
[ptf1] ... [ptfN]
(Node1) (...) (NodeN)
①select * from ptf
②select * from ptf where b = 100;
This test is done with same size of data per node but table is partitioned
across N number of nodes. Each varation (master or patches) is tested
at-least 3 times to get reliable and consistent results. The purpose of the
test is to see impact on performance as number of data nodes are increased.
2.2 The results
For ① result(scalepernode=2G):
nodenumber master patched performance
2 432s 180s 240%
3 636s 223s 285%
4 830s 283s 293%
5 1065s 361s 295%
For ② result(scalepernode=10G):
nodenumber master patched performance
2 281s 140s 201%
3 421s 140s 300%
4 562s 141s 398%
5 702s 141s 497%
6 833s 139s 599%
7 986s 141s 699%
8 1125s 140s 803%
Test Three
This test is similar to the [test one] but with much huge data and
4 nodes.
For ① result:
scalepernode master patched performance
100G 6592s 1649s 399%
For ② result:
scalepernode master patched performance
100G 35383 12363 286%
The result show it work well in much huge data.
Summary
The patch is pretty good, it works well when there were little data back to
the parent node. The patch doesn’t provide parallel FDW scan, it ensures
that child nodes can send data to parent in parallel but the parent can only
sequennly process the data from data nodes.
Providing there is no performance degrdation for non FDW append queries,
I would recomend to consider this patch as an interim soluton while we are
waiting for parallel FDW scan.
Highgo Software (Canada/China/Pakistan)
URL : www.highgo.ca
EMAIL: mailto:movead(dot)li(at)highgo(dot)ca
Attachments:
[application/octet-stream] script.tar (27.5K, ../../[email protected]/3-script.tar)
download
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms
@ 2023-01-17 04:38 Andres Freund <[email protected]>
0 siblings, 0 replies; 261+ messages in thread
From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw)
Until now we used struct timespec on all platforms but windows. Using struct
timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime
overhead (much more complicated additions). Instead we can convert the time to
nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations
cheaper.
Representing time as int64 nanoseconds provides sufficient range, ~292 years
relative to a starting point (depending on clock source, relative to the unix
epoch or the system's boot time). That'd not be sufficient for calendar time
stored on disk, but is plenty for runtime interval time measurement.
On windows instr_time already is represented as cycles. It might make sense to
represent time as cycles on other platforms as well, as using cycle
acquisition instructions like rdtsc directly can reduce the overhead of time
acquisition substantially. This could be done in a fairly localized manner as
the code stands after this commit.
Because the windows and non-windows paths are now more similar, use a common
set of macros. To make that possible, most of the use of LARGE_INTEGER had to
be removed, which looks nicer anyway.
To avoid users of the API relying on the integer representation, we wrap the
64bit integer inside struct struct instr_time.
Author: Andres Freund <[email protected]>
Author: Lukas Fittl <[email protected]>
Author: David Geier <[email protected]>
Reviewed-by: Tom Lane <[email protected]>
Discussion: https://postgr.es/m/[email protected]
---
src/include/portability/instr_time.h | 162 ++++++++++++++-------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h
index 9ea1a68bd94..c0ed491395d 100644
--- a/src/include/portability/instr_time.h
+++ b/src/include/portability/instr_time.h
@@ -34,6 +34,8 @@
*
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
*
+ * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
+ *
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
* only useful on intervals.
@@ -54,8 +56,32 @@
#ifndef INSTR_TIME_H
#define INSTR_TIME_H
+
+/*
+ * We store interval times as an int64 integer on all platforms, as int64 is
+ * cheap to add/subtract, the most common operation for instr_time. The
+ * acquisition of time and converting to specific units of time is platform
+ * specific.
+ *
+ * To avoid users of the API relying on the integer representation, we wrap
+ * the 64bit integer in a struct.
+ */
+typedef struct instr_time
+{
+ int64 ticks; /* in platforms specific unit */
+} instr_time;
+
+
+/* helpers macros used in platform specific code below */
+
+#define NS_PER_S INT64CONST(1000000000)
+#define NS_PER_MS INT64CONST(1000000)
+#define NS_PER_US INT64CONST(1000)
+
+
#ifndef WIN32
+
/* Use clock_gettime() */
#include <time.h>
@@ -80,93 +106,43 @@
#define PG_INSTR_CLOCK CLOCK_REALTIME
#endif
-typedef struct timespec instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_clock_gettime_ns(void)
+{
+ instr_time now;
+ struct timespec tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
+ clock_gettime(PG_INSTR_CLOCK, &tmp);
+ now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
-#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
+ return now;
+}
-#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_clock_gettime_ns())
-#define INSTR_TIME_ADD(x,y) \
- do { \
- (x).tv_sec += (y).tv_sec; \
- (x).tv_nsec += (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (t).ticks)
-#define INSTR_TIME_SUBTRACT(x,y) \
- do { \
- (x).tv_sec -= (y).tv_sec; \
- (x).tv_nsec -= (y).tv_nsec; \
- /* Normalize */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- } while (0)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- do { \
- (x).tv_sec += (y).tv_sec - (z).tv_sec; \
- (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
- /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
- while ((x).tv_nsec < 0) \
- { \
- (x).tv_nsec += 1000000000; \
- (x).tv_sec--; \
- } \
- while ((x).tv_nsec >= 1000000000) \
- { \
- (x).tv_nsec -= 1000000000; \
- (x).tv_sec++; \
- } \
- } while (0)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
#else /* WIN32 */
+
/* Use QueryPerformanceCounter() */
-typedef LARGE_INTEGER instr_time;
+/* helper for INSTR_TIME_SET_CURRENT */
+static inline instr_time
+pg_query_performance_counter(void)
+{
+ instr_time now;
+ LARGE_INTEGER tmp;
-#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
+ QueryPerformanceCounter(&tmp);
+ now.ticks = tmp.QuadPart;
-#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
-
-#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
-
-#define INSTR_TIME_ADD(x,y) \
- ((x).QuadPart += (y).QuadPart)
-
-#define INSTR_TIME_SUBTRACT(x,y) \
- ((x).QuadPart -= (y).QuadPart)
-
-#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
- ((x).QuadPart += (y).QuadPart - (z).QuadPart)
-
-#define INSTR_TIME_GET_DOUBLE(t) \
- (((double) (t).QuadPart) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MILLISEC(t) \
- (((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
-
-#define INSTR_TIME_GET_MICROSEC(t) \
- ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
+ return now;
+}
static inline double
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
return (double) f.QuadPart;
}
+#define INSTR_TIME_SET_CURRENT(t) \
+ ((t) = pg_query_performance_counter())
+
+#define INSTR_TIME_GET_NANOSEC(t) \
+ ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency()))
+
#endif /* WIN32 */
-/* same macro on all platforms */
+
+/*
+ * Common macros
+ */
+
+#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
+
+
+#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
+
+#define INSTR_TIME_ADD(x,y) \
+ ((x).ticks += (y).ticks)
+
+#define INSTR_TIME_SUBTRACT(x,y) \
+ ((x).ticks -= (y).ticks)
+
+#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
+ ((x).ticks += (y).ticks - (z).ticks)
+
+
+#define INSTR_TIME_GET_DOUBLE(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
+
+#define INSTR_TIME_GET_MILLISEC(t) \
+ ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
+
+#define INSTR_TIME_GET_MICROSEC(t) \
+ (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
+
#endif /* INSTR_TIME_H */
--
2.38.0
--vt42kbp2j7rjcapp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch"
^ permalink raw reply [nested|flat] 261+ messages in thread
end of thread, other threads:[~2023-01-17 04:38 UTC | newest]
Thread overview: 261+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2020-01-14 09:12 Re: Re: Append with naive multiplexing of FDWs [email protected] <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]>
2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[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