agora inbox for [email protected]help / color / mirror / Atom feed
Re: @(#) Mordred Labs advisory 0x0001: Buffer overflow in 261+ messages / 2 participants [nested] [flat]
* Re: @(#) Mordred Labs advisory 0x0001: Buffer overflow in @ 2002-08-20 06:34 Dann Corbit <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Dann Corbit @ 2002-08-20 06:34 UTC (permalink / raw) To: Mark Pritchard <[email protected]>; Neil Conway <[email protected]>; +Cc: Justin Clift <[email protected]>; Tom Lane <[email protected]>; Christopher Kings-Lynne <[email protected]>; pgsql-hackers > -----Original Message----- > From: Mark Pritchard [mailto:[email protected]] > Sent: Monday, August 19, 2002 11:27 PM > To: Dann Corbit; Neil Conway > Cc: Justin Clift; Tom Lane; Christopher Kings-Lynne; > [email protected] > Subject: Re: [HACKERS] @(#) Mordred Labs advisory 0x0001: > Buffer overflow in > > > On Tue, 20 Aug 2002 15:35, Dann Corbit wrote: > > Most computer virus problems are caused by buffer overrun. Someone > > decided it wasn't very important. > > > > Some computer viruses have caused billions of dollars in damage. > > Sounds important to me. > > > > "Please try our database. Someday, we hope to close off > all the virus > > entry points, but right now, we figure it isn't too important." > > This sounds a little hysterical to me...don't happen to have > a remotely > accessible database do you? :) I tend to be hyperbolic at times. > > Will you trust your multi-million dollar database to > someone who says > > the above? I think the priorities are upside down. Any *known* > > buffer-overrun _must_ be repaired, and as quickly as possible. And > > As always, feedback accepted in diff -c format. > > Seriously though, Oracle was unbreakable for what, two days? > Software has > bugs. I'm sure there are a stack more in PostgreSQL. > > You limit your exposure to bugs/defects/etc through the use > of multiple layers > of protection. If you leave your database out in the wild, > you deserve to be > hacked. Nobody deserves to be hacked. Security should assume that each link in the chain is the only way to bar the door. IMO-YMMV. > > potential overruns should be identified. A grep for > memcpy, strcpy, > > gets, etc. should hunt down most of them. A known buffer overrun > > should fill the designer of a product with abject terror. And I > > really mean that, literally. If you *know* of a buffer > overrun, and > > simply decide > > I'd be worried if my IT consultants experienced "abject > terror". I much prefer > them to be calm, safe in the knowledge that vulnerabilities > such as this will > not cause me any problems, because they had the forethought > to plan for > situations like this and limit their exposure. My comment was meant to emphasize urgency, rather than irrational behavior. Somewhat hyperbolic, obviously. > I worry about two pieces of software - Apache and OpenSSH. I > compile from > source, knowing that I can fix the issue (be it the recent > issues with either > piece of software) as soon as the fixed source becomes > available. I may be in > the minority, but at least I don't experience abject terror > too often (well, > unless I let my sister drive my car...but that is another story). > > Cheers > > Mark > ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-instr_time-Add-INSTR_TIME_SET_SECONDS-INSTR_TIME_.patch" ^ permalink raw reply [nested|flat] 261+ messages in thread
* [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms @ 2023-01-17 04:38 Andres Freund <[email protected]> 0 siblings, 0 replies; 261+ messages in thread From: Andres Freund @ 2023-01-17 04:38 UTC (permalink / raw) Until now we used struct timespec on all platforms but windows. Using struct timespe causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <[email protected]> Author: Lukas Fittl <[email protected]> Author: David Geier <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] --- src/include/portability/instr_time.h | 162 ++++++++++++++------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/include/portability/instr_time.h b/src/include/portability/instr_time.h index 9ea1a68bd94..c0ed491395d 100644 --- a/src/include/portability/instr_time.h +++ b/src/include/portability/instr_time.h @@ -34,6 +34,8 @@ * * INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds) * + * INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds) + * * Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert * absolute times to intervals. The INSTR_TIME_GET_xxx operations are * only useful on intervals. @@ -54,8 +56,32 @@ #ifndef INSTR_TIME_H #define INSTR_TIME_H + +/* + * We store interval times as an int64 integer on all platforms, as int64 is + * cheap to add/subtract, the most common operation for instr_time. The + * acquisition of time and converting to specific units of time is platform + * specific. + * + * To avoid users of the API relying on the integer representation, we wrap + * the 64bit integer in a struct. + */ +typedef struct instr_time +{ + int64 ticks; /* in platforms specific unit */ +} instr_time; + + +/* helpers macros used in platform specific code below */ + +#define NS_PER_S INT64CONST(1000000000) +#define NS_PER_MS INT64CONST(1000000) +#define NS_PER_US INT64CONST(1000) + + #ifndef WIN32 + /* Use clock_gettime() */ #include <time.h> @@ -80,93 +106,43 @@ #define PG_INSTR_CLOCK CLOCK_REALTIME #endif -typedef struct timespec instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_clock_gettime_ns(void) +{ + instr_time now; + struct timespec tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0) + clock_gettime(PG_INSTR_CLOCK, &tmp); + now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec; -#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0) + return now; +} -#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t))) +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_clock_gettime_ns()) -#define INSTR_TIME_ADD(x,y) \ - do { \ - (x).tv_sec += (y).tv_sec; \ - (x).tv_nsec += (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (t).ticks) -#define INSTR_TIME_SUBTRACT(x,y) \ - do { \ - (x).tv_sec -= (y).tv_sec; \ - (x).tv_nsec -= (y).tv_nsec; \ - /* Normalize */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - } while (0) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - do { \ - (x).tv_sec += (y).tv_sec - (z).tv_sec; \ - (x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \ - /* Normalize after each add to avoid overflow/underflow of tv_nsec */ \ - while ((x).tv_nsec < 0) \ - { \ - (x).tv_nsec += 1000000000; \ - (x).tv_sec--; \ - } \ - while ((x).tv_nsec >= 1000000000) \ - { \ - (x).tv_nsec -= 1000000000; \ - (x).tv_sec++; \ - } \ - } while (0) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0) - -#define INSTR_TIME_GET_MICROSEC(t) \ - (((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000)) #else /* WIN32 */ + /* Use QueryPerformanceCounter() */ -typedef LARGE_INTEGER instr_time; +/* helper for INSTR_TIME_SET_CURRENT */ +static inline instr_time +pg_query_performance_counter(void) +{ + instr_time now; + LARGE_INTEGER tmp; -#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0) + QueryPerformanceCounter(&tmp); + now.ticks = tmp.QuadPart; -#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0) - -#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t)) - -#define INSTR_TIME_ADD(x,y) \ - ((x).QuadPart += (y).QuadPart) - -#define INSTR_TIME_SUBTRACT(x,y) \ - ((x).QuadPart -= (y).QuadPart) - -#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ - ((x).QuadPart += (y).QuadPart - (z).QuadPart) - -#define INSTR_TIME_GET_DOUBLE(t) \ - (((double) (t).QuadPart) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MILLISEC(t) \ - (((double) (t).QuadPart * 1000.0) / GetTimerFrequency()) - -#define INSTR_TIME_GET_MICROSEC(t) \ - ((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency())) + return now; +} static inline double GetTimerFrequency(void) @@ -177,11 +153,45 @@ GetTimerFrequency(void) return (double) f.QuadPart; } +#define INSTR_TIME_SET_CURRENT(t) \ + ((t) = pg_query_performance_counter()) + +#define INSTR_TIME_GET_NANOSEC(t) \ + ((int64) (((double) (t).ticks * NS_PER_S) / GetTimerFrequency())) + #endif /* WIN32 */ -/* same macro on all platforms */ + +/* + * Common macros + */ + +#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0) + + +#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0) #define INSTR_TIME_SET_CURRENT_LAZY(t) \ (INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false) + +#define INSTR_TIME_ADD(x,y) \ + ((x).ticks += (y).ticks) + +#define INSTR_TIME_SUBTRACT(x,y) \ + ((x).ticks -= (y).ticks) + +#define INSTR_TIME_ACCUM_DIFF(x,y,z) \ + ((x).ticks += (y).ticks - (z).ticks) + + +#define INSTR_TIME_GET_DOUBLE(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S) + +#define INSTR_TIME_GET_MILLISEC(t) \ + ((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS) + +#define INSTR_TIME_GET_MICROSEC(t) \ + (INSTR_TIME_GET_NANOSEC(t) / NS_PER_US) + #endif /* INSTR_TIME_H */ -- 2.38.0 --vt42kbp2j7rjcapp Content-Type: text/x-diff; charset=us-ascii Content-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 -- 2002-08-20 06:34 Re: @(#) Mordred Labs advisory 0x0001: Buffer overflow in Dann Corbit <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email protected]> 2023-01-17 04:38 [PATCH v8 2/5] instr_time: Represent time as an int64 on all platforms Andres Freund <[email 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