From 528c2e44ab0a5ba49a5a04689dae3f674b71c15e Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Tue, 10 Mar 2015 22:33:24 -0400 Subject: Move pg_test_timing from contrib/ to src/bin/ Reviewed-by: Michael Paquier --- contrib/Makefile | 1 - contrib/pg_test_timing/.gitignore | 1 - contrib/pg_test_timing/Makefile | 18 -- contrib/pg_test_timing/pg_test_timing.c | 188 ------------------- doc/src/sgml/contrib.sgml | 1 - doc/src/sgml/filelist.sgml | 1 - doc/src/sgml/pgtesttiming.sgml | 308 -------------------------------- doc/src/sgml/ref/allfiles.sgml | 1 + doc/src/sgml/ref/pgtesttiming.sgml | 300 +++++++++++++++++++++++++++++++ doc/src/sgml/reference.sgml | 1 + src/bin/Makefile | 1 + src/bin/pg_test_timing/.gitignore | 1 + src/bin/pg_test_timing/Makefile | 27 +++ src/bin/pg_test_timing/pg_test_timing.c | 188 +++++++++++++++++++ src/tools/msvc/Mkvcbuild.pm | 6 +- 15 files changed, 521 insertions(+), 522 deletions(-) delete mode 100644 contrib/pg_test_timing/.gitignore delete mode 100644 contrib/pg_test_timing/Makefile delete mode 100644 contrib/pg_test_timing/pg_test_timing.c delete mode 100644 doc/src/sgml/pgtesttiming.sgml create mode 100644 doc/src/sgml/ref/pgtesttiming.sgml create mode 100644 src/bin/pg_test_timing/.gitignore create mode 100644 src/bin/pg_test_timing/Makefile create mode 100644 src/bin/pg_test_timing/pg_test_timing.c diff --git a/contrib/Makefile b/contrib/Makefile index 9ca1ed707a..e5ce0be5fd 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -33,7 +33,6 @@ SUBDIRS = \ pg_prewarm \ pg_standby \ pg_stat_statements \ - pg_test_timing \ pg_trgm \ pgcrypto \ pgrowlocks \ diff --git a/contrib/pg_test_timing/.gitignore b/contrib/pg_test_timing/.gitignore deleted file mode 100644 index f6c664c765..0000000000 --- a/contrib/pg_test_timing/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/pg_test_timing diff --git a/contrib/pg_test_timing/Makefile b/contrib/pg_test_timing/Makefile deleted file mode 100644 index 8b37aa8249..0000000000 --- a/contrib/pg_test_timing/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# contrib/pg_test_timing/Makefile - -PGFILEDESC = "pg_test_timing - test timing overhead" -PGAPPICON = win32 - -PROGRAM = pg_test_timing -OBJS = pg_test_timing.o $(WIN32RES) - -ifdef USE_PGXS -PG_CONFIG = pg_config -PGXS := $(shell $(PG_CONFIG) --pgxs) -include $(PGXS) -else -subdir = contrib/pg_test_timing -top_builddir = ../.. -include $(top_builddir)/src/Makefile.global -include $(top_srcdir)/contrib/contrib-global.mk -endif diff --git a/contrib/pg_test_timing/pg_test_timing.c b/contrib/pg_test_timing/pg_test_timing.c deleted file mode 100644 index e5c11de6bb..0000000000 --- a/contrib/pg_test_timing/pg_test_timing.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * pg_test_timing.c - * tests overhead of timing calls and their monotonicity: that - * they always move forward - */ - -#include "postgres_fe.h" - -#include "getopt_long.h" -#include "portability/instr_time.h" - -static const char *progname; - -static int32 test_duration = 3; - -static void handle_args(int argc, char *argv[]); -static uint64 test_timing(int32); -static void output(uint64 loop_count); - -/* record duration in powers of 2 microseconds */ -int64 histogram[32]; - -int -main(int argc, char *argv[]) -{ - uint64 loop_count; - - progname = get_progname(argv[0]); - - handle_args(argc, argv); - - loop_count = test_timing(test_duration); - - output(loop_count); - - return 0; -} - -static void -handle_args(int argc, char *argv[]) -{ - static struct option long_options[] = { - {"duration", required_argument, NULL, 'd'}, - {NULL, 0, NULL, 0} - }; - - int option; /* Command line option */ - int optindex = 0; /* used by getopt_long */ - - if (argc > 1) - { - if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) - { - printf("Usage: %s [-d DURATION]\n", progname); - exit(0); - } - if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) - { - puts("pg_test_timing (PostgreSQL) " PG_VERSION); - exit(0); - } - } - - while ((option = getopt_long(argc, argv, "d:", - long_options, &optindex)) != -1) - { - switch (option) - { - case 'd': - test_duration = atoi(optarg); - break; - - default: - fprintf(stderr, "Try \"%s --help\" for more information.\n", - progname); - exit(1); - break; - } - } - - if (argc > optind) - { - fprintf(stderr, - "%s: too many command-line arguments (first is \"%s\")\n", - progname, argv[optind]); - fprintf(stderr, "Try \"%s --help\" for more information.\n", - progname); - exit(1); - } - - if (test_duration > 0) - { - printf("Testing timing overhead for %d seconds.\n", test_duration); - } - else - { - fprintf(stderr, - "%s: duration must be a positive integer (duration is \"%d\")\n", - progname, test_duration); - fprintf(stderr, "Try \"%s --help\" for more information.\n", - progname); - exit(1); - } -} - -static uint64 -test_timing(int32 duration) -{ - uint64 total_time; - int64 time_elapsed = 0; - uint64 loop_count = 0; - uint64 prev, - cur; - instr_time start_time, - end_time, - temp; - - total_time = duration > 0 ? duration * INT64CONST(1000000) : 0; - - INSTR_TIME_SET_CURRENT(start_time); - cur = INSTR_TIME_GET_MICROSEC(start_time); - - while (time_elapsed < total_time) - { - int32 diff, - bits = 0; - - prev = cur; - INSTR_TIME_SET_CURRENT(temp); - cur = INSTR_TIME_GET_MICROSEC(temp); - diff = cur - prev; - - /* Did time go backwards? */ - if (diff < 0) - { - printf("Detected clock going backwards in time.\n"); - printf("Time warp: %d microseconds\n", diff); - exit(1); - } - - /* What is the highest bit in the time diff? */ - while (diff) - { - diff >>= 1; - bits++; - } - - /* Update appropriate duration bucket */ - histogram[bits]++; - - loop_count++; - INSTR_TIME_SUBTRACT(temp, start_time); - time_elapsed = INSTR_TIME_GET_MICROSEC(temp); - } - - INSTR_TIME_SET_CURRENT(end_time); - - INSTR_TIME_SUBTRACT(end_time, start_time); - - printf("Per loop time including overhead: %0.2f nsec\n", - INSTR_TIME_GET_DOUBLE(end_time) * 1e9 / loop_count); - - return loop_count; -} - -static void -output(uint64 loop_count) -{ - int64 max_bit = 31, - i; - - /* find highest bit value */ - while (max_bit > 0 && histogram[max_bit] == 0) - max_bit--; - - printf("Histogram of timing durations:\n"); - printf("%6s %10s %10s\n", "< usec", "% of total", "count"); - - for (i = 0; i <= max_bit; i++) - { - char buf[100]; - - /* lame hack to work around INT64_FORMAT deficiencies */ - snprintf(buf, sizeof(buf), INT64_FORMAT, histogram[i]); - printf("%6ld %9.5f %10s\n", 1l << i, - (double) histogram[i] * 100 / loop_count, buf); - } -} diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml index b25d18b344..29e4f2df9b 100644 --- a/doc/src/sgml/contrib.sgml +++ b/doc/src/sgml/contrib.sgml @@ -202,7 +202,6 @@ pages. &pgstandby; - &pgtesttiming; &pgxlogdump; diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml index 5095e0fce2..2f4896d4ce 100644 --- a/doc/src/sgml/filelist.sgml +++ b/doc/src/sgml/filelist.sgml @@ -133,7 +133,6 @@ - diff --git a/doc/src/sgml/pgtesttiming.sgml b/doc/src/sgml/pgtesttiming.sgml deleted file mode 100644 index a6ab9b114b..0000000000 --- a/doc/src/sgml/pgtesttiming.sgml +++ /dev/null @@ -1,308 +0,0 @@ - - - - - pg_test_timing - - - - pg_test_timing - 1 - Application - - - - pg_test_timing - measure timing overhead - - - - - pg_test_timing - option - - - - - Description - - - pg_test_timing is a tool to measure the timing overhead - on your system and confirm that the system time never moves backwards. - Systems that are slow to collect timing data can give less accurate - EXPLAIN ANALYZE results. - - - - - Options - - - pg_test_timing accepts the following - command-line options: - - - - - - - - - Specifies the test duration, in seconds. Longer durations - give slightly better accuracy, and are more likely to discover - problems with the system clock moving backwards. The default - test duration is 3 seconds. - - - - - - - - - - Print the pg_test_timing version and exit. - - - - - - - - - - Show help about pg_test_timing command line - arguments, and exit. - - - - - - - - - - - Usage - - - Interpreting results - - - Good results will show most (>90%) individual timing calls take less than - one microsecond. Average per loop overhead will be even lower, below 100 - nanoseconds. This example from an Intel i7-860 system using a TSC clock - source shows excellent performance: - - -Testing timing overhead for 3 seconds. -Per loop time including overhead: 35.96 nsec -Histogram of timing durations: -< usec % of total count - 1 96.40465 80435604 - 2 3.59518 2999652 - 4 0.00015 126 - 8 0.00002 13 - 16 0.00000 2 - - - - - Note that different units are used for the per loop time than the - histogram. The loop can have resolution within a few nanoseconds (nsec), - while the individual timing calls can only resolve down to one microsecond - (usec). - - - - - Measuring executor timing overhead - - - When the query executor is running a statement using - EXPLAIN ANALYZE, individual operations are timed as well - as showing a summary. The overhead of your system can be checked by - counting rows with the psql program: - - -CREATE TABLE t AS SELECT * FROM generate_series(1,100000); -\timing -SELECT COUNT(*) FROM t; -EXPLAIN ANALYZE SELECT COUNT(*) FROM t; - - - - - The i7-860 system measured runs the count query in 9.8 ms while - the EXPLAIN ANALYZE version takes 16.6 ms, each - processing just over 100,000 rows. That 6.8 ms difference means the timing - overhead per row is 68 ns, about twice what pg_test_timing estimated it - would be. Even that relatively small amount of overhead is making the fully - timed count statement take almost 70% longer. On more substantial queries, - the timing overhead would be less problematic. - - - - - - Changing time sources - - On some newer Linux systems, it's possible to change the clock source used - to collect timing data at any time. A second example shows the slowdown - possible from switching to the slower acpi_pm time source, on the same - system used for the fast results above: - - -# cat /sys/devices/system/clocksource/clocksource0/available_clocksource -tsc hpet acpi_pm -# echo acpi_pm > /sys/devices/system/clocksource/clocksource0/current_clocksource -# pg_test_timing -Per loop time including overhead: 722.92 nsec -Histogram of timing durations: -< usec % of total count - 1 27.84870 1155682 - 2 72.05956 2990371 - 4 0.07810 3241 - 8 0.01357 563 - 16 0.00007 3 - - - - - In this configuration, the sample EXPLAIN ANALYZE above - takes 115.9 ms. That's 1061 nsec of timing overhead, again a small multiple - of what's measured directly by this utility. That much timing overhead - means the actual query itself is only taking a tiny fraction of the - accounted for time, most of it is being consumed in overhead instead. In - this configuration, any EXPLAIN ANALYZE totals involving - many timed operations would be inflated significantly by timing overhead. - - - - FreeBSD also allows changing the time source on the fly, and it logs - information about the timer selected during boot: - - -# dmesg | grep "Timecounter" -Timecounter "ACPI-fast" frequency 3579545 Hz quality 900 -Timecounter "i8254" frequency 1193182 Hz quality 0 -Timecounters tick every 10.000 msec -Timecounter "TSC" frequency 2531787134 Hz quality 800 -# sysctl kern.timecounter.hardware=TSC -kern.timecounter.hardware: ACPI-fast -> TSC - - - - - Other systems may only allow setting the time source on boot. On older - Linux systems the "clock" kernel setting is the only way to make this sort - of change. And even on some more recent ones, the only option you'll see - for a clock source is "jiffies". Jiffies are the older Linux software clock - implementation, which can have good resolution when it's backed by fast - enough timing hardware, as in this example: - - -$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource -jiffies -$ dmesg | grep time.c -time.c: Using 3.579545 MHz WALL PM GTOD PIT/TSC timer. -time.c: Detected 2400.153 MHz processor. -$ pg_test_timing -Testing timing overhead for 3 seconds. -Per timing duration including loop overhead: 97.75 ns -Histogram of timing durations: -< usec % of total count - 1 90.23734 27694571 - 2 9.75277 2993204 - 4 0.00981 3010 - 8 0.00007 22 - 16 0.00000 1 - 32 0.00000 1 - - - - - - Clock hardware and timing accuracy - - - Collecting accurate timing information is normally done on computers using - hardware clocks with various levels of accuracy. With some hardware the - operating systems can pass the system clock time almost directly to - programs. A system clock can also be derived from a chip that simply - provides timing interrupts, periodic ticks at some known time interval. In - either case, operating system kernels provide a clock source that hides - these details. But the accuracy of that clock source and how quickly it can - return results varies based on the underlying hardware. - - - - Inaccurate time keeping can result in system instability. Test any change - to the clock source very carefully. Operating system defaults are sometimes - made to favor reliability over best accuracy. And if you are using a virtual - machine, look into the recommended time sources compatible with it. Virtual - hardware faces additional difficulties when emulating timers, and there are - often per operating system settings suggested by vendors. - - - - The Time Stamp Counter (TSC) clock source is the most accurate one available - on current generation CPUs. It's the preferred way to track the system time - when it's supported by the operating system and the TSC clock is - reliable. There are several ways that TSC can fail to provide an accurate - timing source, making it unreliable. Older systems can have a TSC clock that - varies based on the CPU temperature, making it unusable for timing. Trying - to use TSC on some older multicore CPUs can give a reported time that's - inconsistent among multiple cores. This can result in the time going - backwards, a problem this program checks for. And even the newest systems - can fail to provide accurate TSC timing with very aggressive power saving - configurations. - - - - Newer operating systems may check for the known TSC problems and switch to a - slower, more stable clock source when they are seen. If your system - supports TSC time but doesn't default to that, it may be disabled for a good - reason. And some operating systems may not detect all the possible problems - correctly, or will allow using TSC even in situations where it's known to be - inaccurate. - - - - The High Precision Event Timer (HPET) is the preferred timer on systems - where it's available and TSC is not accurate. The timer chip itself is - programmable to allow up to 100 nanosecond resolution, but you may not see - that much accuracy in your system clock. - - - - Advanced Configuration and Power Interface (ACPI) provides a Power - Management (PM) Timer, which Linux refers to as the acpi_pm. The clock - derived from acpi_pm will at best provide 300 nanosecond resolution. - - - - Timers used on older PC hardware include the 8254 Programmable Interval - Timer (PIT), the real-time clock (RTC), the Advanced Programmable Interrupt - Controller (APIC) timer, and the Cyclone timer. These timers aim for - millisecond resolution. - - - - - - Author - - - Ants Aasma ants.aasma@eesti.ee - - - - - See Also - - - - - - diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index e940153540..0e0a1f4f46 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -194,6 +194,7 @@ Complete list of usable sgml source files in this directory. + diff --git a/doc/src/sgml/ref/pgtesttiming.sgml b/doc/src/sgml/ref/pgtesttiming.sgml new file mode 100644 index 0000000000..d5e231fff7 --- /dev/null +++ b/doc/src/sgml/ref/pgtesttiming.sgml @@ -0,0 +1,300 @@ + + + + + pg_test_timing + + + + pg_test_timing + 1 + Application + + + + pg_test_timing + measure timing overhead + + + + + pg_test_timing + option + + + + + Description + + + pg_test_timing is a tool to measure the timing overhead + on your system and confirm that the system time never moves backwards. + Systems that are slow to collect timing data can give less accurate + EXPLAIN ANALYZE results. + + + + + Options + + + pg_test_timing accepts the following + command-line options: + + + + + + + + + Specifies the test duration, in seconds. Longer durations + give slightly better accuracy, and are more likely to discover + problems with the system clock moving backwards. The default + test duration is 3 seconds. + + + + + + + + + + Print the pg_test_timing version and exit. + + + + + + + + + + Show help about pg_test_timing command line + arguments, and exit. + + + + + + + + + + + Usage + + + Interpreting results + + + Good results will show most (>90%) individual timing calls take less than + one microsecond. Average per loop overhead will be even lower, below 100 + nanoseconds. This example from an Intel i7-860 system using a TSC clock + source shows excellent performance: + + +Testing timing overhead for 3 seconds. +Per loop time including overhead: 35.96 nsec +Histogram of timing durations: +< usec % of total count + 1 96.40465 80435604 + 2 3.59518 2999652 + 4 0.00015 126 + 8 0.00002 13 + 16 0.00000 2 + + + + + Note that different units are used for the per loop time than the + histogram. The loop can have resolution within a few nanoseconds (nsec), + while the individual timing calls can only resolve down to one microsecond + (usec). + + + + + Measuring executor timing overhead + + + When the query executor is running a statement using + EXPLAIN ANALYZE, individual operations are timed as well + as showing a summary. The overhead of your system can be checked by + counting rows with the psql program: + + +CREATE TABLE t AS SELECT * FROM generate_series(1,100000); +\timing +SELECT COUNT(*) FROM t; +EXPLAIN ANALYZE SELECT COUNT(*) FROM t; + + + + + The i7-860 system measured runs the count query in 9.8 ms while + the EXPLAIN ANALYZE version takes 16.6 ms, each + processing just over 100,000 rows. That 6.8 ms difference means the timing + overhead per row is 68 ns, about twice what pg_test_timing estimated it + would be. Even that relatively small amount of overhead is making the fully + timed count statement take almost 70% longer. On more substantial queries, + the timing overhead would be less problematic. + + + + + + Changing time sources + + On some newer Linux systems, it's possible to change the clock source used + to collect timing data at any time. A second example shows the slowdown + possible from switching to the slower acpi_pm time source, on the same + system used for the fast results above: + + +# cat /sys/devices/system/clocksource/clocksource0/available_clocksource +tsc hpet acpi_pm +# echo acpi_pm > /sys/devices/system/clocksource/clocksource0/current_clocksource +# pg_test_timing +Per loop time including overhead: 722.92 nsec +Histogram of timing durations: +< usec % of total count + 1 27.84870 1155682 + 2 72.05956 2990371 + 4 0.07810 3241 + 8 0.01357 563 + 16 0.00007 3 + + + + + In this configuration, the sample EXPLAIN ANALYZE above + takes 115.9 ms. That's 1061 nsec of timing overhead, again a small multiple + of what's measured directly by this utility. That much timing overhead + means the actual query itself is only taking a tiny fraction of the + accounted for time, most of it is being consumed in overhead instead. In + this configuration, any EXPLAIN ANALYZE totals involving + many timed operations would be inflated significantly by timing overhead. + + + + FreeBSD also allows changing the time source on the fly, and it logs + information about the timer selected during boot: + + +# dmesg | grep "Timecounter" +Timecounter "ACPI-fast" frequency 3579545 Hz quality 900 +Timecounter "i8254" frequency 1193182 Hz quality 0 +Timecounters tick every 10.000 msec +Timecounter "TSC" frequency 2531787134 Hz quality 800 +# sysctl kern.timecounter.hardware=TSC +kern.timecounter.hardware: ACPI-fast -> TSC + + + + + Other systems may only allow setting the time source on boot. On older + Linux systems the "clock" kernel setting is the only way to make this sort + of change. And even on some more recent ones, the only option you'll see + for a clock source is "jiffies". Jiffies are the older Linux software clock + implementation, which can have good resolution when it's backed by fast + enough timing hardware, as in this example: + + +$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource +jiffies +$ dmesg | grep time.c +time.c: Using 3.579545 MHz WALL PM GTOD PIT/TSC timer. +time.c: Detected 2400.153 MHz processor. +$ pg_test_timing +Testing timing overhead for 3 seconds. +Per timing duration including loop overhead: 97.75 ns +Histogram of timing durations: +< usec % of total count + 1 90.23734 27694571 + 2 9.75277 2993204 + 4 0.00981 3010 + 8 0.00007 22 + 16 0.00000 1 + 32 0.00000 1 + + + + + + Clock hardware and timing accuracy + + + Collecting accurate timing information is normally done on computers using + hardware clocks with various levels of accuracy. With some hardware the + operating systems can pass the system clock time almost directly to + programs. A system clock can also be derived from a chip that simply + provides timing interrupts, periodic ticks at some known time interval. In + either case, operating system kernels provide a clock source that hides + these details. But the accuracy of that clock source and how quickly it can + return results varies based on the underlying hardware. + + + + Inaccurate time keeping can result in system instability. Test any change + to the clock source very carefully. Operating system defaults are sometimes + made to favor reliability over best accuracy. And if you are using a virtual + machine, look into the recommended time sources compatible with it. Virtual + hardware faces additional difficulties when emulating timers, and there are + often per operating system settings suggested by vendors. + + + + The Time Stamp Counter (TSC) clock source is the most accurate one available + on current generation CPUs. It's the preferred way to track the system time + when it's supported by the operating system and the TSC clock is + reliable. There are several ways that TSC can fail to provide an accurate + timing source, making it unreliable. Older systems can have a TSC clock that + varies based on the CPU temperature, making it unusable for timing. Trying + to use TSC on some older multicore CPUs can give a reported time that's + inconsistent among multiple cores. This can result in the time going + backwards, a problem this program checks for. And even the newest systems + can fail to provide accurate TSC timing with very aggressive power saving + configurations. + + + + Newer operating systems may check for the known TSC problems and switch to a + slower, more stable clock source when they are seen. If your system + supports TSC time but doesn't default to that, it may be disabled for a good + reason. And some operating systems may not detect all the possible problems + correctly, or will allow using TSC even in situations where it's known to be + inaccurate. + + + + The High Precision Event Timer (HPET) is the preferred timer on systems + where it's available and TSC is not accurate. The timer chip itself is + programmable to allow up to 100 nanosecond resolution, but you may not see + that much accuracy in your system clock. + + + + Advanced Configuration and Power Interface (ACPI) provides a Power + Management (PM) Timer, which Linux refers to as the acpi_pm. The clock + derived from acpi_pm will at best provide 300 nanosecond resolution. + + + + Timers used on older PC hardware include the 8254 Programmable Interval + Timer (PIT), the real-time clock (RTC), the Advanced Programmable Interrupt + Controller (APIC) timer, and the Cyclone timer. These timers aim for + millisecond resolution. + + + + + + See Also + + + + + + diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index 666493cd64..a526638ec9 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -264,6 +264,7 @@ &pgResetxlog; &pgRewind; &pgtestfsync; + &pgtesttiming; &pgupgrade; &postgres; &postmaster; diff --git a/src/bin/Makefile b/src/bin/Makefile index 06a0ab75b6..d23c95c581 100644 --- a/src/bin/Makefile +++ b/src/bin/Makefile @@ -24,6 +24,7 @@ SUBDIRS = \ pg_resetxlog \ pg_rewind \ pg_test_fsync \ + pg_test_timing \ pg_upgrade \ pgbench \ psql \ diff --git a/src/bin/pg_test_timing/.gitignore b/src/bin/pg_test_timing/.gitignore new file mode 100644 index 0000000000..f6c664c765 --- /dev/null +++ b/src/bin/pg_test_timing/.gitignore @@ -0,0 +1 @@ +/pg_test_timing diff --git a/src/bin/pg_test_timing/Makefile b/src/bin/pg_test_timing/Makefile new file mode 100644 index 0000000000..d1f35954f0 --- /dev/null +++ b/src/bin/pg_test_timing/Makefile @@ -0,0 +1,27 @@ +# src/bin/pg_test_timing/Makefile + +PGFILEDESC = "pg_test_timing - test timing overhead" +PGAPPICON = win32 + +subdir = src/bin/pg_test_timing +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + +OBJS = pg_test_timing.o $(WIN32RES) + +all: pg_test_timing + +pg_test_timing: $(OBJS) | submake-libpgport + $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) + +install: all installdirs + $(INSTALL_PROGRAM) pg_test_timing$(X) '$(DESTDIR)$(bindir)/pg_test_timing$(X)' + +installdirs: + $(MKDIR_P) '$(DESTDIR)$(bindir)' + +uninstall: + rm -f '$(DESTDIR)$(bindir)/pg_test_timing$(X)' + +clean distclean maintainer-clean: + rm -f pg_test_timing$(X) $(OBJS) diff --git a/src/bin/pg_test_timing/pg_test_timing.c b/src/bin/pg_test_timing/pg_test_timing.c new file mode 100644 index 0000000000..e5c11de6bb --- /dev/null +++ b/src/bin/pg_test_timing/pg_test_timing.c @@ -0,0 +1,188 @@ +/* + * pg_test_timing.c + * tests overhead of timing calls and their monotonicity: that + * they always move forward + */ + +#include "postgres_fe.h" + +#include "getopt_long.h" +#include "portability/instr_time.h" + +static const char *progname; + +static int32 test_duration = 3; + +static void handle_args(int argc, char *argv[]); +static uint64 test_timing(int32); +static void output(uint64 loop_count); + +/* record duration in powers of 2 microseconds */ +int64 histogram[32]; + +int +main(int argc, char *argv[]) +{ + uint64 loop_count; + + progname = get_progname(argv[0]); + + handle_args(argc, argv); + + loop_count = test_timing(test_duration); + + output(loop_count); + + return 0; +} + +static void +handle_args(int argc, char *argv[]) +{ + static struct option long_options[] = { + {"duration", required_argument, NULL, 'd'}, + {NULL, 0, NULL, 0} + }; + + int option; /* Command line option */ + int optindex = 0; /* used by getopt_long */ + + if (argc > 1) + { + if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) + { + printf("Usage: %s [-d DURATION]\n", progname); + exit(0); + } + if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) + { + puts("pg_test_timing (PostgreSQL) " PG_VERSION); + exit(0); + } + } + + while ((option = getopt_long(argc, argv, "d:", + long_options, &optindex)) != -1) + { + switch (option) + { + case 'd': + test_duration = atoi(optarg); + break; + + default: + fprintf(stderr, "Try \"%s --help\" for more information.\n", + progname); + exit(1); + break; + } + } + + if (argc > optind) + { + fprintf(stderr, + "%s: too many command-line arguments (first is \"%s\")\n", + progname, argv[optind]); + fprintf(stderr, "Try \"%s --help\" for more information.\n", + progname); + exit(1); + } + + if (test_duration > 0) + { + printf("Testing timing overhead for %d seconds.\n", test_duration); + } + else + { + fprintf(stderr, + "%s: duration must be a positive integer (duration is \"%d\")\n", + progname, test_duration); + fprintf(stderr, "Try \"%s --help\" for more information.\n", + progname); + exit(1); + } +} + +static uint64 +test_timing(int32 duration) +{ + uint64 total_time; + int64 time_elapsed = 0; + uint64 loop_count = 0; + uint64 prev, + cur; + instr_time start_time, + end_time, + temp; + + total_time = duration > 0 ? duration * INT64CONST(1000000) : 0; + + INSTR_TIME_SET_CURRENT(start_time); + cur = INSTR_TIME_GET_MICROSEC(start_time); + + while (time_elapsed < total_time) + { + int32 diff, + bits = 0; + + prev = cur; + INSTR_TIME_SET_CURRENT(temp); + cur = INSTR_TIME_GET_MICROSEC(temp); + diff = cur - prev; + + /* Did time go backwards? */ + if (diff < 0) + { + printf("Detected clock going backwards in time.\n"); + printf("Time warp: %d microseconds\n", diff); + exit(1); + } + + /* What is the highest bit in the time diff? */ + while (diff) + { + diff >>= 1; + bits++; + } + + /* Update appropriate duration bucket */ + histogram[bits]++; + + loop_count++; + INSTR_TIME_SUBTRACT(temp, start_time); + time_elapsed = INSTR_TIME_GET_MICROSEC(temp); + } + + INSTR_TIME_SET_CURRENT(end_time); + + INSTR_TIME_SUBTRACT(end_time, start_time); + + printf("Per loop time including overhead: %0.2f nsec\n", + INSTR_TIME_GET_DOUBLE(end_time) * 1e9 / loop_count); + + return loop_count; +} + +static void +output(uint64 loop_count) +{ + int64 max_bit = 31, + i; + + /* find highest bit value */ + while (max_bit > 0 && histogram[max_bit] == 0) + max_bit--; + + printf("Histogram of timing durations:\n"); + printf("%6s %10s %10s\n", "< usec", "% of total", "count"); + + for (i = 0; i <= max_bit; i++) + { + char buf[100]; + + /* lame hack to work around INT64_FORMAT deficiencies */ + snprintf(buf, sizeof(buf), INT64_FORMAT, histogram[i]); + printf("%6ld %9.5f %10s\n", 1l << i, + (double) histogram[i] * 100 / loop_count, buf); + } +} diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 5b86c805ae..6dd429a8b6 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -35,13 +35,11 @@ my @contrib_uselibpq = my @contrib_uselibpgport = ( 'oid2name', 'pg_standby', - 'pg_test_timing', 'pg_xlogdump', 'vacuumlo'); my @contrib_uselibpgcommon = ( 'oid2name', 'pg_standby', - 'pg_test_timing', 'pg_xlogdump', 'vacuumlo'); my $contrib_extralibs = undef; @@ -55,8 +53,8 @@ my @contrib_excludes = ('pgcrypto', 'commit_ts', 'intagg', 'sepgsql'); # Set of variables for frontend modules my $frontend_defines = { 'initdb' => 'FRONTEND' }; my @frontend_uselibpq = ('pg_ctl', 'pg_upgrade', 'pgbench', 'psql'); -my @frontend_uselibpgport = ( 'pg_archivecleanup', 'pg_test_fsync', 'pg_upgrade', 'pgbench' ); -my @frontend_uselibpgcommon = ( 'pg_archivecleanup', 'pg_test_fsync', 'pg_upgrade', 'pgbench' ); +my @frontend_uselibpgport = ( 'pg_archivecleanup', 'pg_test_fsync', 'pg_test_timing', 'pg_upgrade', 'pgbench' ); +my @frontend_uselibpgcommon = ( 'pg_archivecleanup', 'pg_test_fsync', 'pg_test_timing', 'pg_upgrade', 'pgbench' ); my $frontend_extralibs = { 'initdb' => ['ws2_32.lib'], 'pg_restore' => ['ws2_32.lib'], -- cgit v1.2.3