Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 03023a2

Browse files
committed
instr_time: Represent time as an int64 on all platforms
Until now we used struct timespec for instr_time on all platforms but windows. Using struct timespec causes a fair bit of memory (struct timeval is 16 bytes) and runtime overhead (much more complicated additions). Instead we can convert the time to nanoseconds in INSTR_TIME_SET_CURRENT(), making the remaining operations cheaper. Representing time as int64 nanoseconds provides sufficient range, ~292 years relative to a starting point (depending on clock source, relative to the unix epoch or the system's boot time). That'd not be sufficient for calendar time stored on disk, but is plenty for runtime interval time measurement. On windows instr_time already is represented as cycles. It might make sense to represent time as cycles on other platforms as well, as using cycle acquisition instructions like rdtsc directly can reduce the overhead of time acquisition substantially. This could be done in a fairly localized manner as the code stands after this commit. Because the windows and non-windows paths are now more similar, use a common set of macros. To make that possible, most of the use of LARGE_INTEGER had to be removed, which looks nicer anyway. To avoid users of the API relying on the integer representation, we wrap the 64bit integer inside struct struct instr_time. Author: Andres Freund <andres@anarazel.de> Author: Lukas Fittl <lukas@fittl.com> Author: David Geier <geidav.pg@gmail.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/20230113195547.k4nlrmawpijqwlsa@awork3.anarazel.de
1 parent 25b2aba commit 03023a2

File tree

1 file changed

+86
-76
lines changed

1 file changed

+86
-76
lines changed

src/include/portability/instr_time.h

+86-76
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
*
3535
* INSTR_TIME_GET_MICROSEC(t) convert t to uint64 (in microseconds)
3636
*
37+
* INSTR_TIME_GET_NANOSEC(t) convert t to uint64 (in nanoseconds)
38+
*
3739
* Note that INSTR_TIME_SUBTRACT and INSTR_TIME_ACCUM_DIFF convert
3840
* absolute times to intervals. The INSTR_TIME_GET_xxx operations are
3941
* only useful on intervals.
@@ -54,8 +56,32 @@
5456
#ifndef INSTR_TIME_H
5557
#define INSTR_TIME_H
5658

59+
60+
/*
61+
* We store interval times as an int64 integer on all platforms, as int64 is
62+
* cheap to add/subtract, the most common operation for instr_time. The
63+
* acquisition of time and converting to specific units of time is platform
64+
* specific.
65+
*
66+
* To avoid users of the API relying on the integer representation, we wrap
67+
* the 64bit integer in a struct.
68+
*/
69+
typedef struct instr_time
70+
{
71+
int64 ticks; /* in platforms specific unit */
72+
} instr_time;
73+
74+
75+
/* helpers macros used in platform specific code below */
76+
77+
#define NS_PER_S INT64CONST(1000000000)
78+
#define NS_PER_MS INT64CONST(1000000)
79+
#define NS_PER_US INT64CONST(1000)
80+
81+
5782
#ifndef WIN32
5883

84+
5985
/* Use clock_gettime() */
6086

6187
#include <time.h>
@@ -80,93 +106,43 @@
80106
#define PG_INSTR_CLOCK CLOCK_REALTIME
81107
#endif
82108

83-
typedef struct timespec instr_time;
84-
85-
#define INSTR_TIME_IS_ZERO(t) ((t).tv_nsec == 0 && (t).tv_sec == 0)
86-
87-
#define INSTR_TIME_SET_ZERO(t) ((t).tv_sec = 0, (t).tv_nsec = 0)
88-
89-
#define INSTR_TIME_SET_CURRENT(t) ((void) clock_gettime(PG_INSTR_CLOCK, &(t)))
90-
91-
#define INSTR_TIME_ADD(x,y) \
92-
do { \
93-
(x).tv_sec += (y).tv_sec; \
94-
(x).tv_nsec += (y).tv_nsec; \
95-
/* Normalize */ \
96-
while ((x).tv_nsec >= 1000000000) \
97-
{ \
98-
(x).tv_nsec -= 1000000000; \
99-
(x).tv_sec++; \
100-
} \
101-
} while (0)
109+
/* helper for INSTR_TIME_SET_CURRENT */
110+
static inline instr_time
111+
pg_clock_gettime_ns(void)
112+
{
113+
instr_time now;
114+
struct timespec tmp;
102115

103-
#define INSTR_TIME_SUBTRACT(x,y) \
104-
do { \
105-
(x).tv_sec -= (y).tv_sec; \
106-
(x).tv_nsec -= (y).tv_nsec; \
107-
/* Normalize */ \
108-
while ((x).tv_nsec < 0) \
109-
{ \
110-
(x).tv_nsec += 1000000000; \
111-
(x).tv_sec--; \
112-
} \
113-
} while (0)
116+
clock_gettime(PG_INSTR_CLOCK, &tmp);
117+
now.ticks = tmp.tv_sec * NS_PER_S + tmp.tv_nsec;
114118

115-
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
116-
do { \
117-
(x).tv_sec += (y).tv_sec - (z).tv_sec; \
118-
(x).tv_nsec += (y).tv_nsec - (z).tv_nsec; \
119-
/* Normalize after each add to avoid overflow/underflow of tv_nsec */ \
120-
while ((x).tv_nsec < 0) \
121-
{ \
122-
(x).tv_nsec += 1000000000; \
123-
(x).tv_sec--; \
124-
} \
125-
while ((x).tv_nsec >= 1000000000) \
126-
{ \
127-
(x).tv_nsec -= 1000000000; \
128-
(x).tv_sec++; \
129-
} \
130-
} while (0)
119+
return now;
120+
}
131121

132-
#define INSTR_TIME_GET_DOUBLE(t) \
133-
(((double) (t).tv_sec) + ((double) (t).tv_nsec) / 1000000000.0)
122+
#define INSTR_TIME_SET_CURRENT(t) \
123+
((t) = pg_clock_gettime_ns())
134124

135-
#define INSTR_TIME_GET_MILLISEC(t) \
136-
(((double) (t).tv_sec * 1000.0) + ((double) (t).tv_nsec) / 1000000.0)
125+
#define INSTR_TIME_GET_NANOSEC(t) \
126+
((int64) (t).ticks)
137127

138-
#define INSTR_TIME_GET_MICROSEC(t) \
139-
(((uint64) (t).tv_sec * (uint64) 1000000) + (uint64) ((t).tv_nsec / 1000))
140128

141129
#else /* WIN32 */
142130

143-
/* Use QueryPerformanceCounter() */
144-
145-
typedef LARGE_INTEGER instr_time;
146-
147-
#define INSTR_TIME_IS_ZERO(t) ((t).QuadPart == 0)
148-
149-
#define INSTR_TIME_SET_ZERO(t) ((t).QuadPart = 0)
150-
151-
#define INSTR_TIME_SET_CURRENT(t) QueryPerformanceCounter(&(t))
152-
153-
#define INSTR_TIME_ADD(x,y) \
154-
((x).QuadPart += (y).QuadPart)
155-
156-
#define INSTR_TIME_SUBTRACT(x,y) \
157-
((x).QuadPart -= (y).QuadPart)
158131

159-
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
160-
((x).QuadPart += (y).QuadPart - (z).QuadPart)
132+
/* Use QueryPerformanceCounter() */
161133

162-
#define INSTR_TIME_GET_DOUBLE(t) \
163-
(((double) (t).QuadPart) / GetTimerFrequency())
134+
/* helper for INSTR_TIME_SET_CURRENT */
135+
static inline instr_time
136+
pg_query_performance_counter(void)
137+
{
138+
instr_time now;
139+
LARGE_INTEGER tmp;
164140

165-
#define INSTR_TIME_GET_MILLISEC(t) \
166-
(((double) (t).QuadPart * 1000.0) / GetTimerFrequency())
141+
QueryPerformanceCounter(&tmp);
142+
now.ticks = tmp.QuadPart;
167143

168-
#define INSTR_TIME_GET_MICROSEC(t) \
169-
((uint64) (((double) (t).QuadPart * 1000000.0) / GetTimerFrequency()))
144+
return now;
145+
}
170146

171147
static inline double
172148
GetTimerFrequency(void)
@@ -177,11 +153,45 @@ GetTimerFrequency(void)
177153
return (double) f.QuadPart;
178154
}
179155

156+
#define INSTR_TIME_SET_CURRENT(t) \
157+
((t) = pg_query_performance_counter())
158+
159+
#define INSTR_TIME_GET_NANOSEC(t) \
160+
((int64) ((t).ticks * ((double) NS_PER_S / GetTimerFrequency())))
161+
180162
#endif /* WIN32 */
181163

182-
/* same macro on all platforms */
164+
165+
/*
166+
* Common macros
167+
*/
168+
169+
#define INSTR_TIME_IS_ZERO(t) ((t).ticks == 0)
170+
171+
172+
#define INSTR_TIME_SET_ZERO(t) ((t).ticks = 0)
183173

184174
#define INSTR_TIME_SET_CURRENT_LAZY(t) \
185175
(INSTR_TIME_IS_ZERO(t) ? INSTR_TIME_SET_CURRENT(t), true : false)
186176

177+
178+
#define INSTR_TIME_ADD(x,y) \
179+
((x).ticks += (y).ticks)
180+
181+
#define INSTR_TIME_SUBTRACT(x,y) \
182+
((x).ticks -= (y).ticks)
183+
184+
#define INSTR_TIME_ACCUM_DIFF(x,y,z) \
185+
((x).ticks += (y).ticks - (z).ticks)
186+
187+
188+
#define INSTR_TIME_GET_DOUBLE(t) \
189+
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_S)
190+
191+
#define INSTR_TIME_GET_MILLISEC(t) \
192+
((double) INSTR_TIME_GET_NANOSEC(t) / NS_PER_MS)
193+
194+
#define INSTR_TIME_GET_MICROSEC(t) \
195+
(INSTR_TIME_GET_NANOSEC(t) / NS_PER_US)
196+
187197
#endif /* INSTR_TIME_H */

0 commit comments

Comments
 (0)