PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pg_test_timing.c
Go to the documentation of this file.
1/*
2 * pg_test_timing.c
3 * tests overhead of timing calls and their monotonicity: that
4 * they always move forward
5 */
6
7#include "postgres_fe.h"
8
9#include <limits.h>
10
11#include "getopt_long.h"
12#include "port/pg_bitutils.h"
14
15static const char *progname;
16
17static unsigned int test_duration = 3;
18static double max_rprct = 99.99;
19
20/* record duration in powers of 2 nanoseconds */
21static long long int histogram[64];
22
23/* record counts of first 10K durations directly */
24#define NUM_DIRECT 10000
25static long long int direct_histogram[NUM_DIRECT];
26
27/* separately record highest observed duration */
29static long long int largest_diff_count;
30
31
32static void handle_args(int argc, char *argv[]);
33static void test_system_timing(void);
34#if PG_INSTR_TSC_CLOCK
35static void test_tsc_timing(void);
36#endif
38static void output(uint64 loop_count);
39
40int
41main(int argc, char *argv[])
42{
43 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_test_timing"));
44 progname = get_progname(argv[0]);
45
46 handle_args(argc, argv);
47
48 /* initialize timing infrastructure (required for INSTR_* calls) */
50
52
53#if PG_INSTR_TSC_CLOCK
55#endif
56
57 return 0;
58}
59
60static void
61handle_args(int argc, char *argv[])
62{
63 static struct option long_options[] = {
64 {"duration", required_argument, NULL, 'd'},
65 {"cutoff", required_argument, NULL, 'c'},
66 {NULL, 0, NULL, 0}
67 };
68
69 int option; /* Command line option */
70 int optindex = 0; /* used by getopt_long */
71 unsigned long optval; /* used for option parsing */
72 char *endptr;
73
74 if (argc > 1)
75 {
76 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
77 {
78 printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
79 exit(0);
80 }
81 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
82 {
83 puts("pg_test_timing (PostgreSQL) " PG_VERSION);
84 exit(0);
85 }
86 }
87
88 while ((option = getopt_long(argc, argv, "d:c:",
89 long_options, &optindex)) != -1)
90 {
91 switch (option)
92 {
93 case 'd':
94 errno = 0;
95 optval = strtoul(optarg, &endptr, 10);
96
97 if (endptr == optarg || *endptr != '\0' ||
98 errno != 0 || optval != (unsigned int) optval)
99 {
100 fprintf(stderr, _("%s: invalid argument for option %s\n"),
101 progname, "--duration");
102 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
103 exit(1);
104 }
105
106 test_duration = (unsigned int) optval;
107 if (test_duration == 0)
108 {
109 fprintf(stderr, _("%s: %s must be in range %u..%u\n"),
110 progname, "--duration", 1, UINT_MAX);
111 exit(1);
112 }
113 break;
114
115 case 'c':
116 errno = 0;
117 max_rprct = strtod(optarg, &endptr);
118
119 if (endptr == optarg || *endptr != '\0' || errno != 0)
120 {
121 fprintf(stderr, _("%s: invalid argument for option %s\n"),
122 progname, "--cutoff");
123 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
124 exit(1);
125 }
126
128 {
129 fprintf(stderr, _("%s: %s must be in range %u..%u\n"),
130 progname, "--cutoff", 0, 100);
131 exit(1);
132 }
133 break;
134
135 default:
136 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
137 progname);
138 exit(1);
139 break;
140 }
141 }
142
143 if (argc > optind)
144 {
146 _("%s: too many command-line arguments (first is \"%s\")\n"),
147 progname, argv[optind]);
148 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
149 progname);
150 exit(1);
151 }
152
153 printf(ngettext("Testing timing overhead for %u second.\n\n",
154 "Testing timing overhead for %u seconds.\n\n",
157}
158
159/*
160 * This tests default (non-fast) timing code. A clock source for that is
161 * always available. Hence, we can unconditionally output the result.
162 */
163static void
171
172/*
173 * If on a supported architecture, test the TSC clock source. This clock
174 * source is not always available. In that case we print an informational
175 * message indicating as such.
176 *
177 * We first emit "slow" timings (RDTSCP on x86), which are used for higher
178 * precision measurements when the TSC clock source is enabled. We emit
179 * "fast" timings second (RDTSC on x86), which is used for faster timing
180 * measurements with lower precision.
181 */
182#if PG_INSTR_TSC_CLOCK
183static void
184test_tsc_timing(void)
185{
188
189 printf("\n");
191 if (loop_count > 0)
192 {
194 printf("\n");
195
196 /* Now, emit fast timing measurements */
199 printf("\n");
200
201 printf(_("TSC frequency in use: %u kHz\n"), timing_tsc_frequency_khz);
202
204 if (calibrated_freq > 0)
205 printf(_("TSC frequency from calibration: %u kHz\n"), calibrated_freq);
206 else
207 printf(_("TSC calibration did not converge\n"));
208
211 printf(_("TSC clock source will be used by default, unless timing_clock_source is set to 'system'.\n"));
212 else
213 printf(_("TSC clock source will not be used by default, unless timing_clock_source is set to 'tsc'.\n"));
214 }
215 else
216 printf(_("TSC clock source is not usable. Likely unable to determine TSC frequency. Are you running in an unsupported virtualized environment?\n"));
217}
218#endif
219
220static uint64
222{
223 uint64 loop_count = 0;
225 end_time,
226 prev,
227 cur;
228 const char *time_source = NULL;
229
231 return 0;
232
234
235#if PG_INSTR_TSC_CLOCK
238#endif
239
240 if (fast_timing)
241 printf(_("Fast clock source: %s\n"), time_source);
243 printf(_("System clock source: %s\n"), time_source);
244 else
245 printf(_("Clock source: %s\n"), time_source);
246
247 /*
248 * Pre-zero the statistics data structures. They're already zero by
249 * default, but this helps bring them into processor cache and avoid
250 * possible timing glitches due to COW behavior.
251 */
253 memset(histogram, 0, sizeof(histogram));
254 largest_diff = 0;
256
258 cur = start_time;
259
262
263 while (INSTR_TIME_GT(end_time, cur))
264 {
265 int64 diff;
266 int32 bits;
268
269 prev = cur;
270
271 if (fast_timing)
273 else
275
276 diff_time = cur;
279
280 /* Did time go backwards? */
281 if (unlikely(diff < 0))
282 {
283 fprintf(stderr, _("Detected clock going backwards in time.\n"));
284 fprintf(stderr, _("Time warp: %" PRId64 " ns\n"), diff);
285 exit(1);
286 }
287
288 /* What is the highest bit in the time diff? */
289 if (diff > 0)
290 {
291 bits = pg_leftmost_one_pos64(diff) + 1;
292 /* histogram should be defined large enough */
293 Assert(bits < lengthof(histogram));
294 }
295 else
296 bits = 0;
297
298 /* Update appropriate duration bucket */
299 histogram[bits]++;
300
301 /* Update direct histogram of time diffs */
302 if (diff < NUM_DIRECT)
304
305 /* Also track the largest observed duration, even if >= NUM_DIRECT */
306 if (diff > largest_diff)
307 {
310 }
311 else if (diff == largest_diff)
313
314 loop_count++;
315 }
316
317 /* Refresh end time to be the actual time spent (vs the target end time) */
319
321
322 printf(_("Average loop time including overhead: %0.2f ns\n"),
324
325 return loop_count;
326}
327
328static void
330{
331 int max_bit = lengthof(histogram) - 1;
332 const char *header1 = _("<= ns");
333 const char *header1b = _("ns");
334 const char *header2 = /* xgettext:no-c-format */ _("% of total");
335 const char *header3 = /* xgettext:no-c-format */ _("running %");
336 const char *header4 = _("count");
337 int len1 = strlen(header1);
338 int len2 = strlen(header2);
339 int len3 = strlen(header3);
340 int len4 = strlen(header4);
341 double rprct;
342 bool stopped = false;
343
344 /* find highest bit value */
345 while (max_bit > 0 && histogram[max_bit] == 0)
346 max_bit--;
347
348 /* set minimum column widths */
349 len1 = Max(19, len1);
350 len2 = Max(10, len2);
351 len3 = Max(10, len3);
352 len4 = Max(10, len4);
353
354 printf(_("Histogram of timing durations:\n"));
355 printf("%*s %*s %*s %*s\n",
356 len1, header1,
357 len2, header2,
358 len3, header3,
359 len4, header4);
360
361 rprct = 0;
362 for (int i = 0; i <= max_bit; i++)
363 {
364 double prct = (double) histogram[i] * 100 / loop_count;
365
366 rprct += prct;
367 printf("%*llu %*.4f %*.4f %*lld\n",
368 len1, (1ULL << i) - 1,
369 len2, prct,
370 len3, rprct,
371 len4, histogram[i]);
372 }
373
374 printf(_("\nObserved timing durations up to %.4f%%:\n"), max_rprct);
375 printf("%*s %*s %*s %*s\n",
376 len1, header1b,
377 len2, header2,
378 len3, header3,
379 len4, header4);
380
381 rprct = 0;
382 for (int i = 0; i < NUM_DIRECT; i++)
383 {
384 if (direct_histogram[i])
385 {
386 double prct = (double) direct_histogram[i] * 100 / loop_count;
387 bool print_it = !stopped;
388
389 rprct += prct;
390
391 /* if largest diff is < NUM_DIRECT, be sure we print it */
392 if (i == largest_diff)
393 {
394 if (stopped)
395 printf("...\n");
396 print_it = true;
397 }
398
399 if (print_it)
400 printf("%*d %*.4f %*.4f %*lld\n",
401 len1, i,
402 len2, prct,
403 len3, rprct,
405 if (rprct >= max_rprct)
406 stopped = true;
407 }
408 }
409
410 /* print largest diff when it's outside the array range */
412 {
413 double prct = (double) largest_diff_count * 100 / loop_count;
414
415 printf("...\n");
416 printf("%*lld %*.4f %*.4f %*lld\n",
417 len1, (long long) largest_diff,
418 len2, prct,
419 len3, 100.0,
421 }
422}
#define ngettext(s, p, n)
Definition c.h:1270
#define Max(x, y)
Definition c.h:1085
#define Assert(condition)
Definition c.h:943
#define PG_TEXTDOMAIN(domain)
Definition c.h:1303
int64_t int64
Definition c.h:621
int32_t int32
Definition c.h:620
uint64_t uint64
Definition c.h:625
#define unlikely(x)
Definition c.h:438
uint32_t uint32
Definition c.h:624
#define lengthof(array)
Definition c.h:873
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition exec.c:430
int main(void)
#define fprintf(file, fmt, msg)
Definition cubescan.l:21
struct cursor * cur
Definition ecpg.c:29
#define _(x)
Definition elog.c:96
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition getopt_long.c:60
#define required_argument
Definition getopt_long.h:26
FILE * output
void pg_initialize_timing(void)
Definition instr_time.c:82
int32 timing_tsc_frequency_khz
Definition instr_time.c:67
bool pg_set_timing_clock_source(TimingClockSourceType source)
Definition instr_time.c:92
#define PG_INSTR_SYSTEM_CLOCK_NAME
Definition instr_time.h:216
#define INSTR_TIME_SET_CURRENT(t)
Definition instr_time.h:426
#define INSTR_TIME_GT(x, y)
Definition instr_time.h:442
#define INSTR_TIME_GET_NANOSEC(t)
Definition instr_time.h:445
#define INSTR_TIME_GET_DOUBLE(t)
Definition instr_time.h:448
#define INSTR_TIME_SET_CURRENT_FAST(t)
Definition instr_time.h:423
#define INSTR_TIME_SUBTRACT(x, y)
Definition instr_time.h:436
#define INSTR_TIME_ADD_NANOSEC(t, n)
Definition instr_time.h:433
static TimingClockSourceType pg_current_timing_clock_source(void)
Definition instr_time.h:178
TimingClockSourceType
Definition instr_time.h:124
@ TIMING_CLOCK_SOURCE_SYSTEM
Definition instr_time.h:126
@ TIMING_CLOCK_SOURCE_AUTO
Definition instr_time.h:125
int i
Definition isn.c:77
static int pg_leftmost_one_pos64(uint64 word)
Definition pg_bitutils.h:72
static time_t start_time
Definition pg_ctl.c:96
PGDLLIMPORT int optind
Definition getopt.c:47
PGDLLIMPORT char * optarg
Definition getopt.c:49
static rewind_source * source
Definition pg_rewind.c:89
static void test_system_timing(void)
static void handle_args(int argc, char *argv[])
static long long int largest_diff_count
static uint64 test_timing(unsigned int duration, TimingClockSourceType source, bool fast_timing)
static double max_rprct
#define NUM_DIRECT
static long long int histogram[64]
static const char * progname
static int64 largest_diff
static unsigned int test_duration
static long long int direct_histogram[NUM_DIRECT]
static int64 end_time
Definition pgbench.c:176
static int duration
Definition pgbench.c:175
const char * get_progname(const char *argv0)
Definition path.c:652
#define printf(...)
Definition port.h:266
static int fb(int x)
#define NS_PER_S
Definition uuid.c:31