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#include <math.h>
11
12#include "getopt_long.h"
13#include "port/pg_bitutils.h"
15
16static const char *progname;
17
18static unsigned int test_duration = 3;
19static double max_rprct = 99.99;
20
21/* record duration in powers of 2 nanoseconds */
22static long long int histogram[64];
23
24/* record counts of first 10K durations directly */
25#define NUM_DIRECT 10000
26static long long int direct_histogram[NUM_DIRECT];
27
28/* separately record highest observed duration */
30static long long int largest_diff_count;
31
32
33static void handle_args(int argc, char *argv[]);
34static void test_system_timing(void);
35#if PG_INSTR_TSC_CLOCK
36static void test_tsc_timing(void);
37#endif
39static void output(uint64 loop_count);
40
41int
42main(int argc, char *argv[])
43{
44 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_test_timing"));
45 progname = get_progname(argv[0]);
46
47 handle_args(argc, argv);
48
49 /* initialize timing infrastructure (required for INSTR_* calls) */
51
53
54#if PG_INSTR_TSC_CLOCK
56#endif
57
58 return 0;
59}
60
61static void
62handle_args(int argc, char *argv[])
63{
64 static struct option long_options[] = {
65 {"duration", required_argument, NULL, 'd'},
66 {"cutoff", required_argument, NULL, 'c'},
67 {NULL, 0, NULL, 0}
68 };
69
70 int option; /* Command line option */
71 int optindex = 0; /* used by getopt_long */
72 unsigned long optval; /* used for option parsing */
73 char *endptr;
74
75 if (argc > 1)
76 {
77 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
78 {
79 printf(_("Usage: %s [-d DURATION] [-c CUTOFF]\n"), progname);
80 exit(0);
81 }
82 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
83 {
84 puts("pg_test_timing (PostgreSQL) " PG_VERSION);
85 exit(0);
86 }
87 }
88
89 while ((option = getopt_long(argc, argv, "d:c:",
90 long_options, &optindex)) != -1)
91 {
92 switch (option)
93 {
94 case 'd':
95 errno = 0;
96 optval = strtoul(optarg, &endptr, 10);
97
98 if (endptr == optarg || *endptr != '\0' ||
99 errno != 0 || optval != (unsigned int) optval)
100 {
101 fprintf(stderr, _("%s: invalid argument for option %s\n"),
102 progname, "--duration");
103 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
104 exit(1);
105 }
106
107 test_duration = (unsigned int) optval;
108 if (test_duration == 0)
109 {
110 fprintf(stderr, _("%s: %s must be in range %u..%u\n"),
111 progname, "--duration", 1, UINT_MAX);
112 exit(1);
113 }
114 break;
115
116 case 'c':
117 errno = 0;
118 max_rprct = strtod(optarg, &endptr);
119
120 if (endptr == optarg || *endptr != '\0' || errno != 0)
121 {
122 fprintf(stderr, _("%s: invalid argument for option %s\n"),
123 progname, "--cutoff");
124 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
125 exit(1);
126 }
127
129 {
130 fprintf(stderr, _("%s: %s must be in range %u..%u\n"),
131 progname, "--cutoff", 0, 100);
132 exit(1);
133 }
134 break;
135
136 default:
137 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
138 progname);
139 exit(1);
140 break;
141 }
142 }
143
144 if (argc > optind)
145 {
147 _("%s: too many command-line arguments (first is \"%s\")\n"),
148 progname, argv[optind]);
149 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
150 progname);
151 exit(1);
152 }
153
154 printf(ngettext("Testing timing overhead for %u second.\n\n",
155 "Testing timing overhead for %u seconds.\n\n",
158}
159
160/*
161 * This tests default (non-fast) timing code. A clock source for that is
162 * always available. Hence, we can unconditionally output the result.
163 */
164static void
172
173/*
174 * If on a supported architecture, test the TSC clock source. This clock
175 * source is not always available. In that case we print an informational
176 * message indicating as such.
177 *
178 * We first emit "slow" timings (RDTSCP on x86), which are used for higher
179 * precision measurements when the TSC clock source is enabled. We emit
180 * "fast" timings second (RDTSC on x86), which is used for faster timing
181 * measurements with lower precision.
182 */
183#if PG_INSTR_TSC_CLOCK
184static void
185test_tsc_timing(void)
186{
188 const TscClockSourceInfo *info;
189
190 printf("\n");
192 if (loop_count > 0)
193 {
195 printf("\n");
196
197 /* Now, emit fast timing measurements */
200 printf("\n");
201 }
202
203 /*
204 * Report TSC information regardless of whether it was usable, makes
205 * debugging a lot easier.
206 */
208 if (info->frequency_source[0] != '\0')
209 printf(_("TSC frequency source: %s\n"), info->frequency_source);
210 printf(_("TSC frequency in use: %d kHz\n"), info->frequency_khz);
211
212 if (info->calibrated_frequency_khz > 0)
213 {
214 double diff_pct;
215
216 printf(_("TSC frequency from calibration: %d kHz\n"), info->calibrated_frequency_khz);
217
218 diff_pct = fabs((double) info->calibrated_frequency_khz - info->frequency_khz) /
219 info->frequency_khz * 100.0;
220
221 if (diff_pct > 10.0)
222 {
223 printf(_("WARNING: Calibrated TSC frequency differs by %.1f%% from the TSC frequency in use\n"),
224 diff_pct);
225 printf(_("HINT: Consider setting timing_clock_source to 'system'. Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
226 exit(1);
227 }
228 }
229 else
230 printf(_("TSC calibration did not converge\n"));
231
232 /*
233 * Report whether TSC was usable and, if so, whether it will be used
234 * automatically.
235 */
236 if (loop_count > 0)
237 {
240 printf(_("\nTSC clock source will be used by default, unless timing_clock_source is set to 'system'.\n"));
241 else
242 printf(_("\nTSC clock source will not be used by default, unless timing_clock_source is set to 'tsc'.\n"));
243 }
244 else
245 printf(_("\nTSC clock source is not usable. Likely unable to determine TSC frequency. Are you running in an unsupported virtualized environment?\n"));
246}
247#endif
248
249static uint64
251{
252 uint64 loop_count = 0;
254 end_time,
255 prev,
256 cur;
257 const char *time_source = NULL;
258
260 return 0;
261
263
264#if PG_INSTR_TSC_CLOCK
267#endif
268
269 if (fast_timing)
270 printf(_("Fast clock source: %s\n"), time_source);
272 printf(_("System clock source: %s\n"), time_source);
273 else
274 printf(_("Clock source: %s\n"), time_source);
275
276 /*
277 * Pre-zero the statistics data structures. They're already zero by
278 * default, but this helps bring them into processor cache and avoid
279 * possible timing glitches due to COW behavior.
280 */
282 memset(histogram, 0, sizeof(histogram));
283 largest_diff = 0;
285
287 cur = start_time;
288
291
292 while (INSTR_TIME_GT(end_time, cur))
293 {
294 int64 diff;
295 int32 bits;
297
298 prev = cur;
299
300 if (fast_timing)
302 else
304
305 diff_time = cur;
308
309 /* Did time go backwards? */
310 if (unlikely(diff < 0))
311 {
312 fprintf(stderr, _("Detected clock going backwards in time.\n"));
313 fprintf(stderr, _("Time warp: %" PRId64 " ns\n"), diff);
314 exit(1);
315 }
316
317 /* What is the highest bit in the time diff? */
318 if (diff > 0)
319 {
320 bits = pg_leftmost_one_pos64(diff) + 1;
321 /* histogram should be defined large enough */
322 Assert(bits < lengthof(histogram));
323 }
324 else
325 bits = 0;
326
327 /* Update appropriate duration bucket */
328 histogram[bits]++;
329
330 /* Update direct histogram of time diffs */
331 if (diff < NUM_DIRECT)
333
334 /* Also track the largest observed duration, even if >= NUM_DIRECT */
335 if (diff > largest_diff)
336 {
339 }
340 else if (diff == largest_diff)
342
343 loop_count++;
344 }
345
346 /* Refresh end time to be the actual time spent (vs the target end time) */
348
350
351 printf(_("Average loop time including overhead: %0.2f ns\n"),
353
354 return loop_count;
355}
356
357static void
359{
360 int max_bit = lengthof(histogram) - 1;
361 const char *header1 = _("<= ns");
362 const char *header1b = _("ns");
363 const char *header2 = /* xgettext:no-c-format */ _("% of total");
364 const char *header3 = /* xgettext:no-c-format */ _("running %");
365 const char *header4 = _("count");
366 int len1 = strlen(header1);
367 int len2 = strlen(header2);
368 int len3 = strlen(header3);
369 int len4 = strlen(header4);
370 double rprct;
371 bool stopped = false;
372
373 if (loop_count == 0)
374 {
375 printf(_("WARNING: No timing measurements collected. Report this as a bug to <%s>.\n"), PACKAGE_BUGREPORT);
376 return;
377 }
378
379 /* find highest bit value */
380 while (max_bit > 0 && histogram[max_bit] == 0)
381 max_bit--;
382
383 /* set minimum column widths */
384 len1 = Max(19, len1);
385 len2 = Max(10, len2);
386 len3 = Max(10, len3);
387 len4 = Max(10, len4);
388
389 printf(_("Histogram of timing durations:\n"));
390 printf("%*s %*s %*s %*s\n",
391 len1, header1,
392 len2, header2,
393 len3, header3,
394 len4, header4);
395
396 rprct = 0;
397 for (int i = 0; i <= max_bit; i++)
398 {
399 double prct = (double) histogram[i] * 100 / loop_count;
400
401 rprct += prct;
402 printf("%*llu %*.4f %*.4f %*lld\n",
403 len1, (1ULL << i) - 1,
404 len2, prct,
405 len3, rprct,
406 len4, histogram[i]);
407 }
408
409 printf(_("\nObserved timing durations up to %.4f%%:\n"), max_rprct);
410 printf("%*s %*s %*s %*s\n",
411 len1, header1b,
412 len2, header2,
413 len3, header3,
414 len4, header4);
415
416 rprct = 0;
417 for (int i = 0; i < NUM_DIRECT; i++)
418 {
419 if (direct_histogram[i])
420 {
421 double prct = (double) direct_histogram[i] * 100 / loop_count;
422 bool print_it = !stopped;
423
424 rprct += prct;
425
426 /* if largest diff is < NUM_DIRECT, be sure we print it */
427 if (i == largest_diff)
428 {
429 if (stopped)
430 printf("...\n");
431 print_it = true;
432 }
433
434 if (print_it)
435 printf("%*d %*.4f %*.4f %*lld\n",
436 len1, i,
437 len2, prct,
438 len3, rprct,
440 if (rprct >= max_rprct)
441 stopped = true;
442 }
443 }
444
445 /* print largest diff when it's outside the array range */
447 {
448 double prct = (double) largest_diff_count * 100 / loop_count;
449
450 printf("...\n");
451 printf("%*lld %*.4f %*.4f %*lld\n",
452 len1, (long long) largest_diff,
453 len2, prct,
454 len3, 100.0,
456 }
457}
#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
#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:84
bool pg_set_timing_clock_source(TimingClockSourceType source)
Definition instr_time.c:94
#define PG_INSTR_SYSTEM_CLOCK_NAME
Definition instr_time.h:224
#define INSTR_TIME_SET_CURRENT(t)
Definition instr_time.h:434
#define INSTR_TIME_GT(x, y)
Definition instr_time.h:450
#define INSTR_TIME_GET_NANOSEC(t)
Definition instr_time.h:453
#define INSTR_TIME_GET_DOUBLE(t)
Definition instr_time.h:456
#define INSTR_TIME_SET_CURRENT_FAST(t)
Definition instr_time.h:431
#define INSTR_TIME_SUBTRACT(x, y)
Definition instr_time.h:444
#define INSTR_TIME_ADD_NANOSEC(t, n)
Definition instr_time.h:441
static TimingClockSourceType pg_current_timing_clock_source(void)
Definition instr_time.h:186
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:669
#define printf(...)
Definition port.h:267
static int fb(int x)
#define NS_PER_S
Definition uuid.c:31